轻松突击ThreadLocal

来源:互联网 发布:windows 10如何截图 编辑:程序博客网 时间:2024/06/12 15:43

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

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();    }}

客户端中将

//不使用ThreadLocalprivate static NumberConstruct numberConstruct=new ConcreteNumberConstructA();

替换为

//使用ThreadLocalprivate 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();    }}

将客户端中的

//使用ThreadLocalprivate static NumberConstruct numberConstruct=new ConcreteNumberConstructB();

替换为

//使用自己的MyThreadLocalprivate static NumberConstruct numberConstruct=new ConcreteNumberConstructC();

结果:

这里写图片描述

总结:如果你需要多个线程之间共享变量的时候,想下是否需要考虑线程安全的问题,如果需要则可以使用ThreadLocal简单解决。

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

0 0
原创粉丝点击