ThreadLocal简介
ThreadLocal
一. 概述
ThreadLocal(是Thread Local Variable,线程局部变量)类是Java为线程安全提供的一个工具类,代表一个线程局部变量。把数据放在ThreadLocal中可以让每个线程创建一个该变量的副本,线程间可以独立地改变自己的副本,而不会和其他线程产生副本冲突,从而避免并发访问的线程安全问题,就像每个线程都完全拥有该变量一样。
方法:
- T get() 返回此线程局部变量的当前线程副本值
- void remove() 删除此线程局部变量的当前线程的副本值
- void set(T value) 设置此线程局部变量中当前线程副本值
二. 案例
package myThread;
/**
* 若进程间不需要数据共享,则可使用此方法
* 避免再去设计安全同步
*/
public class ThreadLocalTest {
public static void main(String[] args) {
Account acc = new Account("初始名");
/*
虽然两个线程共享一个账户,但该账户名是ThreadLocal类型
每个线程拥有自己的副本,互不影响
*/
new MyTest(acc,"线程甲").start();
new MyTest(acc,"线程乙").start();
}
}
class Account{
/*
定义一个ThreadLocal类型的线程局部变量(伪装成变量的对象),
每个线程保存一个该变量的副本。
*/
private ThreadLocal<String> name=new ThreadLocal<String>();
public Account(String name){
this.name.set(name);
System.out.println("---"+this.name.get());
}
public String getName(){
return this.name.get();
}
public void setName(String str) {
this.name.set(str);
}
}
class MyTest extends Thread{
//定义一个Account的成员变量
private Account account;
public MyTest(Account account,String name){
super(name);
this.account=account;
}
/**
* 进入run后,线程局部变量会被copy为默认值
* string->null
*/
public void run(){
for (int i = 0; i < 10; i++) {
if(i==6){
/*
此处的getName()是Thread的静态方法
用线程名改变了原先的副本值(null)
*/
account.setName(getName());
}
System.out.println(account.getName()+"账户的i值:"+i);
}
}
}
三. 总结
ThreadLocal和其他所有同步机制一样,都是为了解决多线程中对同一变量的访问冲突。不同的是,在普通同步机制中,是通过对象加锁来实现安全访问的,而ThreadLocal从另一个角度来解决多线程的并发访问。简单说ThreadLocal就是一种以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了。
两者面向问题的领域不同,不能互相代替。
问: ThreadLocal需要注意什么?
使用ThreadLocal要注意remove!
ThreadLocal 的 实 现 是 基 于 一 个 所 谓 的 ThreadLocalMap,在 ThreadLocalMap中 , 它的 key 是 一 个 弱引用。通常 弱引用都会和引用队列配合清理机制使用,但是ThreadLocal 是个例外 ,它并没有这么做。这意味着 ,废弃项目的回收依 赖于显式地触发,否则就要等待线程结束 ,进而回收相应ThreadLocalMap!这就是很多OOM(内存溢出) 的来源 ,所以通常都会建议应用一定要自己负责remove,并且不要和线程池配合 ,因为worker线程往往是不会退出的。