1、ThreadLocal, ThreadLocalMap和Thread之间的关系
在ThreadLocal中共有4个可供调用的方法
1、protected initialValue()
2、public T get()
3、public void set(T)
4、public void remove()
而ThreadLocalMap是ThreadLocal中的内部静态类,其中的Entry是继承自弱引用WeakReference<ThreadLocal>
static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } }
可以看到ThreadLocal.ThreadLocalMap.Entry的键正是ThreadLocal变量
而在Thread类中有一个ThreadLocal.ThreadLocalMap的变量threadLocals,通过这里我们就能比较清楚的了解到ThreadLocal的用意,即为当前线程保存应用所需的局部变量,并且只能为该线程所使用,而且在键ThreadLocal实例对应的值value不再被引用时会因为弱引用的关系而被垃圾回收机制回收,节省内存空间的使用
2、应用场景实战
在网上看了很多人关于ThreadLocal的解释,当然该类的设计也是为了实际应用,官方的Java Doc并没有解释的很清楚,只是提到了一般的用法,我个人觉得以下两篇文章举出的两个实例可以让大家有更加清楚的理解
第一篇:http://www.cnblogs.com/alphablox/archive/2013/01/20/2869061.html
第二篇:http://guoba6688-sina-com.iteye.com/blog/721383
第一篇文章没有具体应用场景,所以在第二篇文章中使用数据库访问的应用大家就能清楚地看到ThreadLocal发挥的作用了。
即在单实例而多线程情况下,该单实例中的某个(也可以是多个)变量不希望被多线程共享,而是希望每个线程有一个单独的实例,如上面第二篇文章介绍的每个UserDAO实例中的Connection,因为多个线程共用一个Connection会造成资源竞争激烈并且使用效率低下,所以为每个线程单独创建一个局部于该线程的Connection实例。
如上面提到的两篇文章,在单例类中,定义static final ThreadLocal变量(因为一个threadLocal变量对应一个局部变量,所以如果有多个局部变量供线程使用的话就定义多个ThreadLocal变量;另外一种做法是泛型变量用Map类型替代,把局部变量放进该Map中即可),其中泛型参数替换为想要保存的类型,如
static ThreadLocal<SomeClass> = new ThreadLocal<SomeClass>();
注意到上面initialValue是一个protected的方法,是建议大家覆盖该方法的实现,默认实现如下:
protected T initialValue() { return null; }
仅仅是返回null,如果不覆盖实现,那么在第一次set()之前调用get()方法时返回的对象就是null,所以我们在代码中就要做这样的判断,并处理,就如同上面第二篇文章中的实现一样,每次getConnection都会去判断(如果不存在,则new一个对象出来,并调用set方法进行设置),特别麻烦。而如果覆盖了initialValue()方法,则该方法只会调用一次,这样你在自己的实现类中就不需要自己添加判断处理逻辑了(参考第一篇文章)。
static ThreadLocal<SomeClass> = new ThreadLocal<SomeClass>(){ @Override public SomeClass initialValue() { return new SomeClass(...); } };
在get操作时会自动进行判断
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
这样在每个线程在调用get操作时都会生成一个属于该线程自己的局部变量。
3、总结
简单总结一下,ThreadLocal确实是用在多线程应用程序当中,但是它不是为了解决多线程并发访问问题,而是为了解决特定问题(多线程访问单例类中的非共享变量)而产生的。这和synchronized同步为了控制多线程访问共享变量的机制不同。
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/15242.html