使用ThreadLocal对共享变量进行封装,可使该共享变量对其他线程不可见。每个线程都会有一个该共享变量的副本,线程对自己的变量副本操作,不会影响到其他线程。以达到线程隔离,线程安全的目的。
一、用法
在使用层面,类的静态变量或被多个线程共享的对象实例的内部属性,可通过ThreadLocal进行包装来实现线程的独立性和线程安全。
例如以下案例:定义一个被所有线程共享的,类型为Integer的计数器。最终的计算结果均符合预期,线程之间并未发生数据被覆盖等问题。
public class ThreadLocalDemo {
//计数器,使用ThreadLocal进行封装。
private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){
//初始值为0
@Override
protected Integer initialValue(){
return 0;
}
};
public static void main(String[] args){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10000000;i++){
int num = count.get();
num++;
count.set(num);
}
System.out.println("线程1的最终结果:"+count.get());
}
});
thread.setName("线程1");
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<20000000;i++){
int num = count.get();
num++;
count.set(num);
}
System.out.println("线程2的最终结果:"+count.get());
}
});
thread1.setName("线程2");
thread.start();
thread1.start();
}
}
ThreadLocal是基于每个线程对象内部的一个叫做threadLocals的属性来实现的,它的类型是ThreadLocalMap(说白了就是一个Map对象)。它以ThreadLocal本本身作为键值,副本对象作为value存储,这样当每个线程调用该对象时就可以直接从自身的threadLocals属性中获取变量副本来进行操作
二、注意事项
因为因为每个线程都包含ThreadLocal封装的共享变量副本,所以会额外占用一定的内存,随着线程数越来越大,占用的内存也会越来越大。所以在使用时,应结合真实情况,若共享变量数据太大,则建议使用synchronized关键字或java并发包下的线程安全实现,如:HashMap对应的ConcurrentHashMap、Integer对应的AtomicInteger等。