zoukankan      html  css  js  c++  java
  • 轻松突击ThreadLocal

    本文出自
    代码大湿
    代码大湿

    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简单解决。

    本文出自
    代码大湿
    代码大湿

  • 相关阅读:
    九月三十学习报告
    九月二十九学习报告
    九月二十八学习报告
    九月二十七学习报告
    九月二十五学习报告
    九月二十四学习报告
    九月二十三学习报告
    九月二十二学习报告
    九月二十学习报告
    网络协议中HTTP,TCP,UDP,Socket,WebSocket的优缺点/区别
  • 原文地址:https://www.cnblogs.com/pin-wang/p/5815349.html
Copyright © 2011-2022 走看看