简介
java中经常使用ThreadLocal作为处理高并发访问的可选手段,ThreadLocal并不是一个线程,而是”以线程为作用域“的一种面向对象的数据结构。其用法的也有着让人难以理解的怪异性。
代码实战
定义一个线程类,其中定义了一个ThreadLocal 的对象
class AutoIntegerThread extends Thread
{
//静态局部变量(注意java中的静态变量的生命周期,共享性,唯一性)
private static ThreadLocal autoInteger = 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
{
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==>1
ait_2==>1
ait_2==>2
ait_2==>3
ait_1==>2
ait_3==>1
ait_1==>3
ait_3==>2
ait_1==>4
ait_3==>3
ait_1==>5
有上面的代码和代码注释可知,符合java中的静态变量的描述,【静态变量的生命周期和应用程序的生命周期完全相同。静态变量具有共享性,唯一性,也就是说是单例】
是的,静态变量可以说是一个完整的单例,但由打印结果可知,似乎情况却违反了这种逻辑。
首先仔细想想,单例的好处是降低了内存的使用,从而提高程序的执行效率(这里不可过分理解,其实,当静态变量太多时反而也会影响内存的回收,因为他的生命周期决定了他和程序时“同寿的”,静态变量尽量少用的好,万事讲究一个“度”),另外单例的唯一性说明了他非常适合做“同步锁”。
其次,从结果来看,貌似谁也没影响谁。有人质疑是不是 private 的问题,建议java基础不熟练的话就不要问这样的问题了,因为private只决定访问权限,而不决定变量的创建。因此也就说明了,以线程为作用域。
if(autoInteger.get()==null)
{
autoInteger.set(new Integer(0));
}
从这段代码来看,如果是List集合来说,只会运行一次,但程序似乎并没有这么做,而是分别在每个线程个执行了一次。
看看源代码
java.lang.ThreadLocal
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通过“线程域”可以实现作用域的区分,并使得程序更加高效