`
javantsky
  • 浏览: 83659 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

白话ThreadLocal类

阅读更多

ThreadLocal类在Spring等框架中扮演了一个很重要的角色,本文就从源码的角度揭开它羞涩的面纱。

废话不少,先看看可以怎么使用:

Person类:

public class Person {
	private String name;
	private int age;

	public Person(String name, int age)
	{
		this.name = name;
		this.age = age;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

 TLPerson类:我理解为Person的线程变量包装类

public class TLPerson {
         //一般采用私有静态的方式定义
	private static ThreadLocal<Person> persontl = new ThreadLocal<Person>()
	{
		protected Person initialValue() {

	        return new Person("zubin" + Thread.currentThread().getId(), 31);

	    }
	};

	public Person getPerson()
	{
		Person p = persontl.get();
		if(p == null)
		{
			persontl.set(new Person("zubin" + Thread.currentThread().getName(),13));
   		        p = persontl.get();
		}
		return p;
	}

	public void setPerson(Person p)
	{
		persontl.set(p);
	}
}

 线程测试类:大家注意run方法中没有同步块

public class ThreadRunnable implements Runnable {
	TLPerson tlp;
	TLPerson tlp2;

	public ThreadRunnable(TLPerson tlp, TLPerson tlp2)
	{
		this.tlp = tlp;
		this.tlp2 = tlp2;
	}

	public void run() {
		System.out.println(tlp.getPerson().getName() + " : " +tlp.getPerson().getAge());
		tlp2.setPerson(new Person("abc" + Thread.currentThread().getId() ,33));
		System.out.println(tlp2.getPerson().getName() + " : " +tlp2.getPerson().getAge());
	}

	public static void main(String ... args)
	{
		TLPerson tlp = new TLPerson();
		TLPerson tlp2 = new TLPerson();

		ThreadRunnable tr1 = new ThreadRunnable(tlp, tlp2);
		ThreadRunnable tr2 = new ThreadRunnable(tlp, tlp2);

		new Thread(tr1).start();
		new Thread(tr2).start();
	}
}

 运行下测试线程,结果如下:

zubin7 : 31
abc7 : 33
zubin8 : 31
abc8 : 33

从结果可以看出,虽然两个线程都使用了相同的“TLPerson”,但是互相之间并无影响。


为什么呢,先来看ThreadLocal和Thread的关系


从上图可以看出,ThreadLocal和Thread是通过ThreadLocalMap这个内部类关联在一起的。

look:

public class Thread implements Runnable {

。。。

/*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

。。。

} 

step1:

当我们在创建Thread对象

ThreadRunnable tr1 = new ThreadRunnable(tlp, tlp2);
new Thread(tr1).start();

这时tr1的inheritableThreadLocals为空。

step2:

tr1开始执行run方法,执行

System.out.println(tlp.getPerson().getName() + " : " +tlp.getPerson().getAge());

我们看下tlp.getPerson()是怎么做的?

step3:

public Person getPerson()
    {
        Person p = persontl.get();
        if(p == null)
        {
            persontl.set(new Person("zubin" + Thread.currentThread().getName(),13));
            p = persontl.get();
        }

        return p;
    }

 从 persontl.get()方法进去了,我们看一下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();
    }

    获取当前线程t的map,这个时候肯定是为null,所以会执行 setInitialValue()方法

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    首先通过 initialValue()方法获取初始值,这个方法我们在匿名内部类做了重写

 

private static ThreadLocal<Person> persontl = new ThreadLocal<Person>()
	{
		protected Person initialValue() {
	        return new Person("zubin" + Thread.currentThread().getId(), 31);
	    }
	};

   然后去获取map,ThreadLocalMap map = getMap(t);这个时候map还是为空,进入createMap()方法

 

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

  在这个方法里才给Thread创建了map,并且把new Person("zubin" + Thread.currentThread().getId(), 31);对象存入map中。

  注意了,是new的一个对象哟,这是理解ThreadLocal的关键

 

		ThreadRunnable tr2 = new ThreadRunnable(tlp, tlp2);
		new Thread(tr2).start();

 tr2重复了step1、2、3.

 

O(∩_∩)O哈哈~,到次我们发现每个线程都有一个map,里面存放的以ThreadLocal对象为key,new的“共享”变量为value。也就是说每个线程的对象都是自己私人的,当然也就不会存在同步的问题了。

好处就是以空间换时间了,没有锁竞争了。

 

 

2
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics