[JAVA学习笔记-96]ThreadLocal

来源:互联网 发布:淘宝售假仅退款不退货 编辑:程序博客网 时间:2024/05/22 03:02
综述:
ThreadLocal可以理解为一个对象容器,这个容器为每个访问该容器的线程,封装了一个只对当前线程可见的对象。
也就是说,当多个线程共用一段代码时(例如同一个runnable对象分配给多个线程运行),ThreadLocal封装的对象
将会是线程安全的,只是访问的时候,需要用到set、get、remove、initValue等方法,否则,这些对象将会是多线程共享的,
是非线程安全的。


实现方法:
在Thread类中实现了一个 threadLocalMap<ThreadLocal key,T value>,执行ThreadLocal.set(T value)时,实质是获得当前Thread的这个 threadLocalMap ,将ThreadLocal对象作为Key(实质
是将该对象的HashCode作为Key)计算出下标(threadLocalMap是一个Entry[]),将指定的值存入数组中下标对应的位置。
    在执行 ThreadLocal.get 时,再次获取当前Thread的 threadLocalMap,通过 ThreadLocal 对象作为key索引到保存的value,并返回。

【1、ThreadLocal 的 set方法:】

public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);  /*如果当前线程没有创建这个map,则创建一个。*/    }



【2、ThreadLocal 的 getMap方法:】

ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }



【3、ThreadLocal 的 createMap 方法:】

void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }



【4、ThreadLocal.ThreadLocalMap 的构造方法:】

ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);  /*使用hashcode计算得到下标*/table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);}private Entry[] table;static class Entry extends WeakReference<ThreadLocal> {    /*注意这里的 WeakReference*//** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal k, Object v) {      /*构造函数*/super(k);value = v;}}




应用场景:
1、 web request handler
例如httpserver的hanler,向server注册一个GEThandler,每条http的GET请求都会调用此handler(假设只有一个handler对象,one thread per request),当并发的时候,handler中的
某些field会被多线程共享,且他们的值需要相互隔离(线程间不关心各自对这些field的修改),此时如果要线程安全,需要用ThreadLocal包装这些field。
2、 Reactor模式下的Accepter
多个连接并发的时候,Accepter可能需要用ThreadLocal隔离一些field,如果只有一个Accepter对象,而不是 new Thread(new Handler()).start(),那么可能需要用到ThreadLocal。

实例:
public class Task implements Runnable{private static AtomicInteger num = new AtomicInteger(0);private ThreadLocal<Integer> id = new ThreadLocal<Integer>();@Overridepublic void run() {// TODO Auto-generated method stubid.set(num.incrementAndGet());   /*每个线程从AtomicInteger返回一个唯一的值*/System.out.println("id="+id.get());  /*每个线程的id值都是唯一的,并且相互之间感知不到,也不需要同步,因为此value实际上是Thread的local variable*/}}




总结:
1、ThreadLocals are even more expensive than globals but they are certainly much better scoped.
2、ThreadLocal is for storing per-thread state past the execution scope of a method. 
3、Another option is using synchronized to manage access to a shared member variable. 
4、ThreadLocal,个人理解的应用场景为,当某个field为多线程共用,而field的值仅对各自线程有意义时,才用ThreadLocal包装隔离。
   如果这个共用的field的值,对所有的线程都有意义,例如A线程修改了它,而B线程需要用到这个修改后的值,那么需要用 synchronized 或者用Atomicxxx族的对象来进行同步;
   如果A线程修改的值,B线程并不关心,B线程只是与A线程共享了一段code而已,那么ThreadLocal可以用上。
   通常情况下,尽量使用local variables在性能上更好。












原创粉丝点击