并发编程(9)threadLocal的用法
2016-03-17 16:39
561 查看
本章我们将讨论ThreadLocal的使用,主要分以下几个章节:
.1)ThreadLocal是什么?
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
它并不是一个Thread,而是threadlocalvariable(线程局部变量,也叫线程本地变量)。
.2)ThreadLocal的使用
啥也不说,先看个例子
创建一个Person对象
那么ThreadLocal的原理具体是什么呢?
我们先来看看ThreadLocal的源码提供的几个方法
.1)set(T value)
而createMap实现为:
我们还注意到ThreadLocalMap代码中有弱引用,如下:
.2)T get()
.3)initialValue()
来看一下源码实现,
通过上面几个方法的分析,我们基本上了解了ThreadLocal的工作原理,每个线程都拥有一个ThreadLocalMap对象,其中ThreadLocal对象为key,value为线程共享对象的副本,这样没有线程访问的都是本线程的变量副本,从而实现了线程间的访问隔离。
.4)remove()
.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>() { @Override protected 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(){ @Override public void run() { Person p=PersonManager.getPerson(); p.setAge(20); System.out.println("Person["+p.getAge()+","+p.getName()+"]"); } }; Thread p2=new Thread(){ @Override public void run() { Person p=PersonManager.getPerson(); p.setAge(21); System.out.println("Person["+p.getAge()+","+p.getName()+"]"); } }; Thread p3=new Thread(){ @Override public 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()); } }执行结果:
19 Person[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); }将当前线程局部变量的值删除,目的是为了减少内存的使用,需要注意的是,当线程结束后,对应线程的局部变量会被垃圾自动回收,所以显示调用该方法进行线程局部变量并不是必须的操作,但是能加快内存回收速度。
相关文章推荐
- C#线程间不能调用剪切板的解决方法
- C#线程同步的三类情景分析
- C#获取进程或线程相关信息的方法
- C#停止线程的方法
- C#子线程更新UI控件的方法实例总结
- C#线程队列用法实例分析
- C++使用CriticalSection实现线程同步实例
- 基于C++实现的线程休眠代码
- VB读取线程、句柄及写入内存的API代码实例
- C#网络编程基础之进程和线程详解
- C#通过Semaphore类控制线程队列的方法
- C#多线程处理多个队列数据的方法
- C#实现线程安全的简易日志记录方法
- C#中线程同步对象的方法分析
- ASP.NET线程相关配置
- 浅析linux环境下一个进程最多能有多少个线程
- 再谈JavaScript线程
- C#实现终止正在执行的线程
- Java线程编程中的主线程讲解
- 解析Java线程同步锁的选择方法