ThreadLocal的一些自己的理解



有关于ThreadLocal的原理的源代码解释在下面已经说的很清楚了 源代码也很清晰易懂

http://www.cnblogs.com/dolphin0520/p/3920407.html



个人对于ThreadLocal类的一些总结:

1.

ThreadLocal类并不是将线程和所存储的对象进行键值保存的和进行取值的(这是我第一反应以为的)

而是将存储的值保存到对应Thread对象的ThreadLocalMap里,ThreadLocalMap的实现原理类似于hashMap,

内部有一个Entry数组,一个Entry通常至少包括key,value, 查找时通过一定的运算规则运算Key的HASH值,来得到Entry在数组中的位置,进而得到相应的value。

但是这个ThreadLocalMap是将当前ThreadLocal对象实例传入当作键值,将set的对象当value值进行存储

下面是ThreadLocal的set方法源码

  public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);//将this传入 
        else
            createMap(t, value);
    }


我们进map的set里看看

  private void set(ThreadLocal key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);//对key进行hash运算 存储

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }


取值时也根据对应的thread对象拿出自己的threadLocalMap,再根据threadLocal的实例为键值得到对应本线程本ThreadLocal的副本

   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();
    }


2

每个threadLocalMap对应每个线程只能存储一个对象

也就是说你set了一个对象A,再放入对象B ,对象B就会把对象A取代 这就跟HashMap一样 

因为ThreadLocal是键值.。通过以上的代码也能看出来 



3.

先看一段代码

public class Test
{
	public int i = 1;
	static ThreadLocal<Object> t =  new ThreadLocal<Object>();
	public static void main(String[] args){
		Test test = new Test();
		Test test1 = new Test();
		MyThread m1= new MyThread(2,test,true);
	//	MyThread m2 = new MyThread(3,test1,false);
		MyThread m2 = new MyThread(3,test,false);
		Thread t1 = new Thread(m1);
		Thread t2 = new Thread(m2);
		t1.start();
		t2.start();

	}
}

class MyThread implements Runnable{
	int obj;
	boolean  flag;
	Test t;
	public MyThread(int i,Test t ,boolean flag){
		this.obj = i;
		this.t=t;
		this.flag=flag;
	}
	
	@Override
	public void run()
	{
		Test.t.set(obj);//该放入的对象被后面的所取代
		Test.t.set(t);//只在run期间threadLocals是保存的 run之后threadLocals置空
		//并不是由threadLocal给你克隆 副本 而是由你自己来设置放进去的副本
		
		Test t = (Test) get();
		if(flag==false){
			try
			{
				Thread.sleep(5000);//睡眠 确保另外一个线程已经操作结束
			} catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("线程为"+Thread.currentThread().getId()+"原来的:"+t.i);
		t.i = obj;//改变值
		System.out.println("线程为"+Thread.currentThread().getId()+"现在的:"+((Test)get()).i);
		System.out.println("线程为"+Thread.currentThread().getId()+"所存储的对象:"+get());
	
	}
	
	public Object get(){
		return Test.t.get();
	}
	
}


 

此时输出如下:

线程为9原来的:1
线程为9现在的:2
线程为9所存储的对象:ThreadLocalTest.Test@65b8b5cd
线程为10原来的:2
线程为10现在的:3
线程为10所存储的对象:ThreadLocalTest.Test@65b8b5cd


我在两个线程放入了同一个Test对象test,当然run方法里也set了这个test

像我刚开始以为ThreadLocal会自动为每个线程创建一个副本(好可笑的梦哈)

这段代码也证实了并不会。

所以并不是由ThreadLocal给你克隆副本,而是由你自己来设置放进去的副本


如果将注释的代码代替

则输出如下:


线程为9原来的:1
线程为9现在的:2
线程为9所存储的对象:ThreadLocalTest.Test@65b8b5cd
线程为10原来的:1
线程为10现在的:3
线程为10所存储的对象:ThreadLocalTest.Test@72d2ee5d


上面啰嗦一大堆 就是为了让我自己认识到ThreadLocal需要自己来设置对应每个线程的副本

而这就需要执行ThreadLocal的set方法 或者重写ThreadLocal的initialValue方法

如同下面这样:

static ThreadLocal<Object> t =  new ThreadLocal<Object>(){
		public Object initialValue(){
			return new Test();
		}
	};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值