您的位置:首页 > 编程语言 > Java开发

[JAVA学习笔记-96]ThreadLocal

2017-06-10 12:39 531 查看
综述:
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>();
@Override
public void run() {
// TODO Auto-generated method stub
id.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在性能上更好。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: