了解Java的Synchronized机制的大家想必都了解过,这个锁有多么的强大和美妙。它就像哪吒有三头六臂,手上拽着一堆法宝。
它可以用在类上,可以用在方法上面,甚至可以用在代码块上面。一个线程需要获取被Synchronized修饰过的方法或者对象,通常需要阻塞等待一下。
就好像大家都需要拿着电影票,挨个排好队,依次通过检票员的验证,才可以进入影厅观看电影。
当你觉得用得很顺手的时候,继续深挖原理,往往会给你当头一盆冷水。

Synchronized机制的使用:
1. 带来了不小的时间消耗。加锁、解锁都需要额外操作。我曾经写过一篇关于python的的锁机制(写得比较简单,有兴趣可以出门左转),大致描述了一下python的锁是怎么获得和释放的。语言之间也是有共通之处的,虽然Java对我们隐藏了Synchronized的实现逻辑,但道理还是差不多的。2、带来了不少的系统开销。互斥同步对性能最大的影响是阻塞的实现,因为阻塞涉及到的挂起线程和恢复线程的操作都需要转入内核态中完成用户态与内核态的切换的性能代价是比较大的,这里就不展开描述了,我自己理解也不是很深刻 话虽这么说,Synchronized有如此强大的机制,当然会有它的一席用武之地。在实际项目中,好比如,获取Jedis实例的时候会用到,redis一般也就是存储一下用户的session,对并发的要求不是非常严格。我们都知道线程执行任务都是在一定的CPU时间片里面进行的。如果大家都阻塞等待了,那未免也太对不起现代的多核CPU了吧。
好了,说了这么多,接下来才是引入今天的主角,ThreadLocal。
为什么说她是各扫门前雪呢?道理很简单,ThreadLocal的实现机制就是在当前线程内部存储自己的一份数据,这份数据不受其他线程的干扰。
有些文章会说是在复制了一份副本存储在线程内部。
ThreadLocal主要有两个方法,get() 和 set()- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) {
- @SuppressWarnings("unchecked")
- T result = (T)e.value;
- return result;
- }
- }
- return setInitialValue();
- }
复制代码- public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
复制代码 从代码不难看出,ThreadLocal内部实际上是定义了一个叫做ThreadLocalMap 的 HashMap,用于存储当前线程的数据
再往下一步,源码对TreadLocalMap有一个比较详尽的描述:- /**<br> * ThreadLocalMap is a customized hash map suitable only for<br> * maintaining thread local values. No operations are exported<br> * outside of the ThreadLocal class. The class is package private to<br> * allow declaration of fields in class Thread. To help deal with<br> * very large and long-lived usages, the hash table entries use<br> * WeakReferences for keys. However, since reference queues are not<br> * used, stale entries are guaranteed to be removed only when<br> * the table starts running out of space.<br> */
复制代码 意思大概是,由于Map使用WeakReference弱引用对象作为key,因为没有使用WeakRefrence的弱引用队列(即key具有唯一性)
WeakRefrence这个东东也是一个很奇妙的东西,有兴趣可以在园子里面逛逛,看看相关介绍啦,我自己也是一步步追溯下来理解的。这里就不展开了。
所以只有当Map的数据非常大,已经超出规定的内存空间的时候才会被GC掉。这样保障了单个复杂操作过程,线程中ThreadLocal数据的稳定。
[code]static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ // 只有当key是null的时候,才会被定义为陈旧的数据被移除
// static class Entry extends WeakReference |