并发编程(9)threadLocal的用法

来源:互联网 发布:达内java课程安排 编辑:程序博客网 时间:2024/06/05 19:27

       本章我们将讨论ThreadLocal的使用,主要分以下几个章节:

.1)ThreadLocal是什么?

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

它并不是一个Thread,而是threadlocalvariable(线程局部变量,也叫线程本地变量)。

.2)ThreadLocal的使用

啥也不说,先看个例子

创建一个Person对象

public class Person {private int age;private String name;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;}}
ThreadLocal处理:
public class PersonManager {private static ThreadLocal<Person> personContor = new ThreadLocal<Person>() {@Overrideprotected Person initialValue() {Person p = new Person();p.setAge(19);p.setName("xgj");return p;}};private static Person getPerson(){return personContor.get();}@SuppressWarnings("unused")private void setPerson(Person p){personContor.set(p);}public static void main(String[] args) throws InterruptedException {Thread p1=new Thread(){@Overridepublic void run() {Person p=PersonManager.getPerson();p.setAge(20);System.out.println("Person["+p.getAge()+","+p.getName()+"]");}};Thread p2=new Thread(){@Overridepublic void run() {Person p=PersonManager.getPerson();p.setAge(21);System.out.println("Person["+p.getAge()+","+p.getName()+"]");}};Thread p3=new Thread(){@Overridepublic void run() {Person p=PersonManager.getPerson();System.out.println("Person["+p.getAge()+","+p.getName()+"]");}};System.out.println(PersonManager.getPerson().getAge());p1.start();p2.start();p3.start();p1.join();p2.join();p3.join();System.out.println(PersonManager.getPerson().getAge());}}
执行结果:
19Person[20,xgj]Person[21,xgj]Person[19,xgj]19
从结果我们可以看出,Main函数在线程p1,p2,p3执行前后Person对象的age值不变,p1,p2,p3获取Person值后修改了age的值,但是3个线程之间互不影响,操作的都是当前线程绑定的person对象。

那么ThreadLocal的原理具体是什么呢?

我们先来看看ThreadLocal的源码提供的几个方法

.1)set(T value)

 public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }
从这个方法中,首先获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,当然如果获取到的ThreadLocalMap对象为空,就通过createMap方法创建。我们再来看看ThreadLocalMap这个对象,ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取,我们可以发现,map.set(this,value),这里的this指向的就是ThreadLocal对象,而值就是你所设置的对象了。我们来看一下getMap和createMap方法的实现
ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }
ThreadLocal.ThreadLocalMap threadLocals = null;

而createMap实现为:

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

我们还注意到ThreadLocalMap代码中有弱引用,如下:

 static class Entry extends WeakReference<ThreadLocal> {            /** The value associated with this ThreadLocal. */            Object value;            Entry(ThreadLocal k, Object v) {                super(k);                value = v;            }        }
大家请注意,之所以用的是弱引用,就是为了在ThreadLocal失去强引用的时候,TreadLocal对应的Entry能够在下次gc时被回收,回收后的空间能够得到复用,在一定的程度下能够避免内存泄露。

.2)T get()

 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();    }
get方法通过当前线程获取到ThreadLocalMap,如果ThreadLocalMap不为空,就根据ThreadLocal key从ThreadLocalMap中获取value值,如果ThreadLocalMap不存在,则调用初始化方法setInitialValue(),我们来看一下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;    }
在此方法中基本上跟set(T value)方法相呼应,请注意这行代码 T value =initialValue(); 这里调用了一个方法 initialValue(),我们下面来分析一下。

.3)initialValue()

来看一下源码实现,

protected T initialValue() {        return null;    }
本方法返回null,说明该方法应该由ThreadLocal子类重写,也就是有ThreadLocal实现类实现T的赋值。

       通过上面几个方法的分析,我们基本上了解了ThreadLocal的工作原理,每个线程都拥有一个ThreadLocalMap对象,其中ThreadLocal对象为key,value为线程共享对象的副本,这样没有线程访问的都是本线程的变量副本,从而实现了线程间的访问隔离。

.4)remove()

 public void remove() {         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)             m.remove(this);     }
将当前线程局部变量的值删除,目的是为了减少内存的使用,需要注意的是,当线程结束后,对应线程的局部变量会被垃圾自动回收,所以显示调用该方法进行线程局部变量并不是必须的操作,但是能加快内存回收速度。






1 0