1. 程式人生 > 實用技巧 >多執行緒:ThreadLocal的基本使用

多執行緒:ThreadLocal的基本使用

1、ThreadLocal初識

  從Java官方文件中的描述:ThreadLocal類用來提供執行緒內部的區域性變數。這種變數在多執行緒環境下訪問(通過get和set方法訪問)時能保證各個執行緒的變數相對獨立於其他執行緒內的變數。ThreadLocal例項通常來說都是private static型別的,用於關聯執行緒和執行緒上下文。
  我們可以得知ThreadLocal的作用是∶提供執行緒內的區域性變數,不同的執行緒之間不會相互干擾,這種變數線上程的生命週期內起作用,減少同一個執行緒內多個函式或元件之間一些公共變數傳遞的複雜度。

  • 執行緒併發:ThreadLocal只在多執行緒的環境下起作用
  • 傳遞資料:我們可以通過ThreadLocal在同一執行緒不同元件中傳遞公共資料
  • 執行緒隔離:每一個執行緒的變數都是獨立的,不會相互影響

2、ThreadLocal的基本使用

(1)常用方法

(2)ThreadLocal的簡單使用

多執行緒的不隔離性:

public class T1 {
    private String content;
    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    
public static void main(String[] args) { T1 t1=new T1(); for(int i=0;i<5;i++){ Thread thread=new Thread(new Runnable() { @Override public void run() { t1.setContent(Thread.currentThread().getName()+"的資料:"); System.
out.println(Thread.currentThread().getName()+"---"+t1.getContent()); } }); thread.setName("執行緒"+i); thread.start(); } } }

測試結果:

執行緒1---執行緒1的資料:
執行緒0---執行緒1的資料:
執行緒2---執行緒2的資料:
執行緒3---執行緒3的資料:
執行緒4---執行緒4的資料:

將變數與當前執行緒繫結:

public class T1 {
    private String content;
    ThreadLocal<String> threadLocal=new ThreadLocal<>();
    public String getContent() {
        return threadLocal.get();
    }

    public void setContent(String content) {
        threadLocal.set(content);
    }

    public static void main(String[] args) {
        T1 t1=new T1();
        for(int  i=0;i<5;i++){
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    t1.setContent(Thread.currentThread().getName()+"的資料:");
                    System.out.println(Thread.currentThread().getName()+"---"+t1.getContent());
                }
            });
            thread.setName("執行緒"+i);
            thread.start();
        }
    }
}

測試結果:

執行緒0---執行緒0的資料:
執行緒1---執行緒1的資料:
執行緒2---執行緒2的資料:
執行緒3---執行緒3的資料:
執行緒4---執行緒4的資料:

多個執行緒的資料是相互隔離的。在多執行緒的場景下,每一個執行緒的變數都相互獨立。例如:執行緒1設定的變數是變數1,那麼獲取到的變數也是變數1。

(3)加鎖

public class T1 {
    private String content;
    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
    public static void main(String[] args) {
        T1 t1=new T1();
        for(int  i=0;i<5;i++){
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (T1.class){
                        t1.setContent(Thread.currentThread().getName()+"的資料:");
                        System.out.println(Thread.currentThread().getName()+"---"+t1.getContent());
                    }
                }
            });
            thread.setName("執行緒"+i);
            thread.start();
        }
    }
}

雖然ThreadLocal模式與synchronized關鍵字都用於處理多執行緒並梭訪問變數的問題,不過兩者處理問題的角度和思路不同。

用ThreadLocal可以使得程式有更好的效能,因為ThreadLocal沒有加鎖

(4)案例

在銀行轉賬的案例中:

傳統解決方案:

保證service層和dao層的連線物件的一致,可以通過將service層的引數傳遞到dao層,但是會提高程式碼的耦合度

需要保證每一個執行緒的連線物件前後一致,也就是說執行緒1的連線物件對應連線1,執行緒2的連線物件對應連線2,可以通過加鎖(synchronized)的方式保證執行緒的隔離,但是會降低程式的效能

ThreadLocal方式:

傳遞資料︰儲存每個執行緒繫結的資料,在需要的地方可以直接獲取,避免參數直接傳遞帶來的程式碼耦合問題
執行緒隔離︰各執行緒之間的資料相互隔離卻又具備併發性,避兔同步方式帶來的效能損失