Java ThreadLocal的用法解析

310 阅读3分钟

简介

java中经常使用ThreadLocal作为处理高并发访问的可选手段,ThreadLocal并不是一个线程,而是”以线程为作用域“的一种面向对象的数据结构。其用法的也有着让人难以理解的怪异性。

代码实战

定义一个线程类,其中定义了一个ThreadLocal 的对象

 class AutoIntegerThread extends Thread
{
	//静态局部变量(注意java中的静态变量的生命周期,共享性,唯一性)
       private static ThreadLocal<Integer> autoInteger = new ThreadLocal<Integer>();
	//自增次数
	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==>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通过“线程域”可以实现作用域的区分,并使得程序更加高效