轻松突击ThreadLocal

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

ThreadLocal是用来保存线程的本地变量,能够保证每一个线程都有一个本身的变量(包括static变量)。git

本文全部代码请点击我github

1 看个实际场景。安全

咱们要设计一个序列号生成器,每一个线程之间对序列号的获取是是隔离的。初始咱们可能会这样设计。使用一个static变量。多线程

首先有一个序列号生成器的接口ide

package ThreadLocal;
/*
 *2016年8月28日    下午2:48:17
 *@Author Pin-Wang
 *@E-mail 1228935432@qq.com
*/
public interface NumberConstruct {
    public int get();
}

生成器的具体实现是:.net

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变量(没有考虑到共享资源的线程安全),这并非咱们想要的结果。code

因此咱们用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简单解决。

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

相关文章
相关标签/搜索