輕鬆突擊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簡單解決。

本文出自
代碼大溼
代碼大溼

相關文章
相關標籤/搜索