ThreadLocal是用来保存线程的本地变量,可以保证每个线程都有一个自己的变量(包括static变量)。
1 看个实际场景。
我们要设计一个序列号生成器,每个线程之间对序列号的获取是是隔离的。初始我们可能会这样设计。使用一个static变量。
首先有一个序列号生成器的接口
package ThreadLocal;
/*
 *2016年8月28日	下午2:48:17
 *@Author Pin-Wang
 *@E-mail 1228935432@qq.com
*/
public interface NumberConstruct {
	public int get();
}
生成器的具体实现是:
package ThreadLocal;
/*
 *2016年8月28日	下午2:49:34
 *@Author Pin-Wang
 *@E-mail 1228935432@qq.com
*/
public class ConcreteNumberConstructA implements NumberConstruct{
	private volatile static int n=0;
	@Override
	public  synchronized int get() {
		return ++n;
	}
}
客户端:
package ThreadLocal;
/*
 *2016年8月28日	下午2:46:10
 *@Author Pin-Wang
 *@E-mail 1228935432@qq.com
*/
public class Test {
	//不使用ThreadLocal
	private static NumberConstruct numberConstruct=new ConcreteNumberConstructA();;
	
	//使用ThreadLocal
	//private static NumberConstruct numberConstruct=new ConcreteNumberConstructB();;
	
	public static void main(String[] args){
		//每个线程获取三个序列号
		Runnable task=new Runnable() {
			public void run() {
				for (int i = 0; i < 3; i++) {
					System.out.println(Thread.currentThread().getName()+" "+numberConstruct.get());
				}
			}
		};
		//开启是哪个线程
		Thread t1=new Thread(task);
		Thread t2=new Thread(task);
		Thread t3=new Thread(task);
		t1.start();
		t2.start();
		t3.start();
	}
	
}
结果;
可以看到3个线程之间都共享了static变量(没有考虑到共享资源的线程安全),这并不是我们想要的结果。
所以我们用ThreadLocal解决:
生成器的具体实现:
package ThreadLocal;
/*
 *2016年8月28日	下午2:49:34
 *@Author Pin-Wang
 *@E-mail 1228935432@qq.com
*/
public class ConcreteNumberConstructB implements NumberConstruct{
	private  static ThreadLocal<Integer> n=new ThreadLocal<Integer>(){
		@Override
		protected Integer initialValue() {
			return 0;
		}};
	
	
	@Override
	public   int get() {
		n.set(n.get()+1);
		return n.get();
	}
}
客户端中将
//不使用ThreadLocal
private static NumberConstruct numberConstruct=new ConcreteNumberConstructA();
	
替换为
//使用ThreadLocal
private static NumberConstruct numberConstruct=new ConcreteNumberConstructB();
	
其它均不变
结果:
这是我们想要的结果。可以看到对于每个共享变量,每个线程之间都有自己的副本,线程之间是隔离的。
2 实现我们自己的ThreadLocal。
ThreadLocal内部其实非常简单。主要是一个同步的HashMap(因为涉及到多线程共享资源),主要有以下几个方法;
//得到当前线程的副本值
get()
//设定当前线程的副本值
set()
//删除当前线程的副本值
remove()
//初始化当前线程的副本值
initialValue()
code;
MyThreadLocal类
package ThreadLocal;
/*
 *2016年8月28日	下午3:57:17
 *@Author Pin-Wang
 *@E-mail 1228935432@qq.com
*/
import java.util.concurrent.ConcurrentHashMap;
public class MyThreadLocal<T> {
	private ConcurrentHashMap<Thread, T> map=new ConcurrentHashMap<>();
	//initialValue()
	protected T initialValue(){
		//返回null,由子类指定初始值
		return null;
	}
	
	//set()
	public void set(T value){
		map.put(Thread.currentThread(), value);
	}
	//get()
	public T get(){
		if(!map.containsKey(Thread.currentThread())){
			T value=initialValue();
			map.put(Thread.currentThread(), value);
		}
		return map.get(Thread.currentThread());
	}
	//remove()
	public void remove(){
		map.remove(Thread.currentThread());
	}
}
ConcreteNumberConstructC 类
package ThreadLocal;
/*
 *2016年8月28日	下午2:49:34
 *@Author Pin-Wang
 *@E-mail 1228935432@qq.com
*/
public class ConcreteNumberConstructC implements NumberConstruct{
	private   MyThreadLocal<Integer> n=new MyThreadLocal<Integer>(){
		@Override
		protected Integer initialValue() {
			return 0;
		}};
	
	
	@Override
	public   int get() {
		n.set(n.get()+1);
		return n.get();
	}
}
将客户端中的
//使用ThreadLocal
private static NumberConstruct numberConstruct=new ConcreteNumberConstructB();
替换为
//使用自己的MyThreadLocal
private static NumberConstruct numberConstruct=new ConcreteNumberConstructC();
结果:
总结:如果你需要多个线程之间共享变量的时候,想下是否需要考虑线程安全的问题,如果需要则可以使用ThreadLocal简单解决。
 
                     
                    
                 
                    
                 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号