ThreadLocal详解
2016-03-08 11:33
591 查看
1.ThreadLocal是什么
在JDK1.2版本中就提供了java.lang.ThreadLocal,ThreadLocal为解决多线程并发提供了新的思路。
ThreadLocal并不是一个Thread,而是一个线程局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
2.ThreadLocal中的接口
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
●void set(T value)
设置当前线程的线程局部变量的值。
●public T get()
该方法返回当前线程所对应的线程局部变量。
●public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
●public T initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
3.自己实现一个ThreadLocal:MyThreadLocal
4.使用自己实现的MyThreadLocal为每个线程产生一组序列号
运行结果为:
thread[Thread-1] sn[1]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[1]
thread[Thread-2] sn[3]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
若改为ThreadLocal运行结果也是一样的。
考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。
在JDK1.2版本中就提供了java.lang.ThreadLocal,ThreadLocal为解决多线程并发提供了新的思路。
ThreadLocal并不是一个Thread,而是一个线程局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
2.ThreadLocal中的接口
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
●void set(T value)
设置当前线程的线程局部变量的值。
●public T get()
该方法返回当前线程所对应的线程局部变量。
●public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
●public T initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
3.自己实现一个ThreadLocal:MyThreadLocal
package com.tqmall; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Created by dingbao on 16/3/8. */ public class MyThreadLocal<T> { //创建一个Map,key为线程对象,value为本线程的变量副本 private Map<Thread,T > threadTMap = Collections.synchronizedMap(new HashMap<Thread, T>()); public void set(T value){ threadTMap.put(Thread.currentThread(),value); } public T get(){ Thread thread = Thread.currentThread(); T value = threadTMap.get(thread); if(value == null && !threadTMap.containsKey(thread)){ value = initialValue(); threadTMap.put(thread,value); } return value; } public void remove(){ threadTMap.remove(Thread.currentThread()); } protected T initialValue(){ return null; } }
4.使用自己实现的MyThreadLocal为每个线程产生一组序列号
package com.tqmall; /** * Created by dingbao on 16/3/8. */ public class SequenceNum { //通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 private static MyThreadLocal<Integer> seqNum = new MyThreadLocal<Integer>(){ public Integer initialValue(){ return 0; } }; //获取下一个序列 public Integer getNextNum(){ seqNum.set(seqNum.get()+1); return seqNum.get(); } public static void main(String[] args){ SequenceNum sequenceNum = new SequenceNum(); Test test = new Test(sequenceNum); Test test1 = new Test(sequenceNum); Test test2 = new Test(sequenceNum); test.start(); test1.start(); test2.start(); } private static class Test extends Thread { private SequenceNum sn; public Test(SequenceNum sn) { this.sn = sn; } @Override public void run() { for (int i = 0; i < 3; i++) { //每个线程打出3个序列值 System.out.println("thread[" + Thread.currentThread().getName() + "] sn[" + sn.getNextNum() + "]"); } } } }
运行结果为:
thread[Thread-1] sn[1]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[1]
thread[Thread-2] sn[3]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
若改为ThreadLocal运行结果也是一样的。
考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树