1.7 ThreadLocal的原理和使用詳解

why:爲什麼要用ThreadLocal

示例:不適用ThreadLocal共同使用變量,使用static,每個值修改同一個變量,產生錯誤

/**
 * 不使用ThreadLocal共同使用變量,使用static,每個值修改同一個變量,產生錯誤
 * @author: honry.guan
 * @create: 2020-06-07 18:53
 **/
public class NoThreadLocalTest extends Thread{
    private static int num = 1;
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 開始執行,num = "+num);
        num  = num + 1;
        System.out.println(Thread.currentThread().getName()+" 結束,num = "+num);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            NoThreadLocalTest t = new NoThreadLocalTest();
            t.start();
        }
    }
}

 運行結果,可能有幾種可能性:

Thread-1: 開始執行,num = 1
Thread-0: 開始執行,num = 1
Thread-0 結束,num = 3
Thread-2: 開始執行,num = 1
Thread-1 結束,num = 2
Thread-2 結束,num = 4

也有可能是:

Thread-1: 開始執行,num = 1
Thread-2: 開始執行,num = 1
Thread-2 結束,num = 2
Thread-0: 開始執行,num = 1
Thread-0 結束,num = 4
Thread-1 結束,num = 4

 使用ThreadLocal

public class NoThreadLocalTest extends Thread{
    private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 開始執行,num = "+num.get());
        num.set(num.get() + 1);
        System.out.println(Thread.currentThread().getName()+" 結束,num = "+num.get());
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            NoThreadLocalTest t = new NoThreadLocalTest();
            t.start();
        }
    }
}

結果:都是2

Thread-0: 開始執行,num = 1
Thread-2: 開始執行,num = 1
Thread-2 結束,num = 2
Thread-1: 開始執行,num = 1
Thread-0 結束,num = 2
Thread-1 結束,num = 2

 

ThreadLocal的使用

ThreadLocal類接口很簡單,常用的有4個方法

• void set(Object value):設置當前線程的線程局部變量的值。

• public Object get():該方法返回當前線程所對應的線程局部變量。

• public void remove():移除變量,如果最開始是1,通過set修改之後,調用remove之後,get出來的值又變成1

• protected Object initialValue():初始,第一次調用set或get才執行,並且僅執行一次。

remove的測試

public class NoThreadLocalTest extends Thread{
    private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 開始執行,num = "+num.get());
        num.set(num.get() + 1);
        System.out.println(Thread.currentThread().getName()+" 結束,num = "+num.get());
        num.remove();
        System.out.println(Thread.currentThread().getName()+" 結束remove之後,num = "+num.get());
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            NoThreadLocalTest t = new NoThreadLocalTest();
            t.start();
        }
    }
}

執行結果 

Thread-1: 開始執行,num = 1
Thread-2: 開始執行,num = 1
Thread-0: 開始執行,num = 1
Thread-0 結束,num = 2
Thread-2 結束,num = 2
Thread-1 結束,num = 2
Thread-1 結束remove之後,num = 1
Thread-2 結束remove之後,num = 1
Thread-0 結束remove之後,num = 1

ThreadLocal初始化方法的使用

1、實現方法initialValue

private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
    @Override
    protected Integer initialValue() {
        return 1;
    }
};

2、通過lomoba表達式

ThreadLocal<Integer> num = ThreadLocal.withInitial(()->1);

ThreadLocal在線程中的使用,即怎麼讓多個線程使用threadLocal

(我剛學的時候一直被這個事情困擾,自己手寫幾次之後明白)

只要在線程run方法中,調用ThreadLocal的get或者set,不管是線程對象內部自定義threadLocal,還是通過構造函數傳遞進線程的ThreadLocal變量。

 

1、線程對象內部自定義threadLocal

public class NoThreadLocalTest extends Thread{
    private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 開始執行,num = "+num.get());
        num.set(num.get() + 1);
        System.out.println(Thread.currentThread().getName()+" 結束,num = "+num.get());
        num.remove();
        System.out.println(Thread.currentThread().getName()+" 結束remove之後,num = "+num.get());
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            NoThreadLocalTest t = new NoThreadLocalTest();
            t.start();
        }
    }
}

通過構造函數傳遞進線程的ThreadLocal變量

public class NoThreadLocalTest extends Thread{
    private ThreadLocal<Integer> num ;

    public NoThreadLocalTest(ThreadLocal<Integer> num) {
        this.num = num;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 開始執行,num = "+num.get());
        num.set(num.get() + 1);
        System.out.println(Thread.currentThread().getName()+" 結束,num = "+num.get());
        num.remove();
        System.out.println(Thread.currentThread().getName()+" 結束remove之後,num = "+num.get());
    }

    public static void main(String[] args) {
        ThreadLocal<Integer> num = ThreadLocal.withInitial(()->1);
    }
}

此時不論什麼一個線程能夠併發訪問這個變量,對它進行寫入、讀取操作,都是線程安全的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章