简介
java中经常使用ThreadLocal作为处理高并发访问的可选手段,ThreadLocal并不是一个线程,而是”以线程为作用域“的一种面向对象的数据结构。其用法的也有着让人难以理解的怪异性。
代码实战
定义一个线程类,其中定义了一个ThreadLocal<Integer> 的对象
class AutoIntegerThread extends Thread{ //静态局部变量(注意java中的静态变量的生命周期,共享性,唯一性) private static ThreadLocalautoInteger = new ThreadLocal (); //自增次数 private int runTimes; public AutoIntegerThread(int runTimes) { this.runTimes = runTimes; } @Override public void run() { //为了说明ThreadLocal以线程为作用域,这里不适用同步代码块 if(autoInteger.get()==null) { autoInteger.set(new Integer(0)); } for (int i = 0; i < runTimes; i++) { autoInteger.set(autoInteger.get()+1); //这里使用以下,保证完整打印一下 synchronized (autoInteger) { System.out.println(getName()+"==>"+autoInteger.get().intValue()); } } } public Integer getFinalInteger() { return autoInteger.get(); }}
测试代码
public class TestThreadLocal { public static void main(String[] args) { AutoIntegerThread ait_1 = new AutoIntegerThread(5); AutoIntegerThread ait_2 = new AutoIntegerThread(3); AutoIntegerThread ait_3 = new AutoIntegerThread(3); ait_1.setName("ait_1"); ait_2.setName("ait_3"); ait_3.setName("ait_2"); ait_1.start(); ait_2.start(); ait_3.start(); }}
打印结果
ait_1==>1ait_2==>1ait_2==>2ait_2==>3ait_1==>2ait_3==>1ait_1==>3ait_3==>2ait_1==>4ait_3==>3ait_1==>5
有上面的代码和代码注释可知,符合java中的静态变量的描述,【静态变量的生命周期和应用程序的生命周期完全相同。静态变量具有共享性,唯一性,也就是说是单例】
是的,静态变量可以说是一个完整的单例,但由打印结果可知,似乎情况却违反了这种逻辑。
首先仔细想想,单例的好处是降低了内存的使用,从而提高程序的执行效率(这里不可过分理解,其实,当静态变量太多时反而也会影响内存的回收,因为他的生命周期决定了他和程序时“同寿的”,静态变量尽量少用的好,万事讲究一个“度”),另外单例的唯一性说明了他非常适合做“同步锁”。
其次,从结果来看,貌似谁也没影响谁。有人质疑是不是 private 的问题,建议java基础不熟练的话就不要问这样的问题了,因为private只决定访问权限,而不决定变量的创建。因此也就说明了,以线程为作用域。
if(autoInteger.get()==null){ autoInteger.set(new Integer(0));}
从这段代码来看,如果是List集合来说,只会运行一次,但程序似乎并没有这么做,而是分别在每个线程个执行了一次。
看看源代码
java.lang.ThreadLocal<T>
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } 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(); }
很显然,这个域作为map的key而存在,也就是说以“线程域”来创建和读取数据
而这个map的是属于Thread,看下面的源码
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
然而,进一步证明了threadlocal使用线程域
java.lang.Thread
ThreadLocal.ThreadLocalMap threadLocals = null;
static class ThreadLocalMap {....}
反正是个map,版面估计不足,不粘了,大家可以去看看。
总结:ThreadLocal通过“线程域”可以实现作用域的区分,并使得程序更加高效