Java ThreadLocal 範例

Java ThreadLocal 用於創建線程本地變數。我們知道一個對象的所有線程共享它的變數,因此該變數不是線程安全的。我們可以使用同步實現線程安全,但如果要避免同步,我們可以使用 ThreadLocal 變數。

Java ThreadLocal

每個線程都有自己的 ThreadLocal 變數,它們可以使用其 get() 和 set() 方法獲取默認值或更改其在該線程中的值。ThreadLocal 實例通常是希望與線程關聯狀態的類中的私有靜態字段。

Java ThreadLocal Example

這是一個小例子,展示了在 Java 程序中使用 ThreadLocal 並證明每個線程都有自己的 ThreadLocal 變數的用法。 ThreadLocalExample.java

package com.journaldev.threads;

import java.text.SimpleDateFormat;
import java.util.Random;

public class ThreadLocalExample implements Runnable{

    // SimpleDateFormat 不是線程安全的,因此每個線程都有一個
    private static final ThreadLocal formatter = new ThreadLocal(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };
    
    public static void main(String[] args) throws InterruptedException {
        ThreadLocalExample obj = new ThreadLocalExample();
        for(int i=0 ; i<10; i++){
            Thread t = new Thread(obj, ""+i);
            Thread.sleep(new Random().nextInt(1000));
            t.start();
        }
    }

    @Override
    public void run() {
        System.out.println("Thread Name= "+Thread.currentThread().getName()+" default Formatter = "+formatter.get().toPattern());
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //formatter 模式在此由線程更改,但不會反映到其他線程
        formatter.set(new SimpleDateFormat());
        
        System.out.println("Thread Name= "+Thread.currentThread().getName()+" formatter = "+formatter.get().toPattern());
    }

}

上面的 Java ThreadLocal 示例程序的輸出是:

Thread Name= 0 default Formatter = yyyyMMdd HHmm
Thread Name= 1 default Formatter = yyyyMMdd HHmm
Thread Name= 0 formatter = M/d/yy h:mm a
Thread Name= 2 default Formatter = yyyyMMdd HHmm
Thread Name= 1 formatter = M/d/yy h:mm a
Thread Name= 3 default Formatter = yyyyMMdd HHmm
Thread Name= 4 default Formatter = yyyyMMdd HHmm
Thread Name= 4 formatter = M/d/yy h:mm a
Thread Name= 5 default Formatter = yyyyMMdd HHmm
Thread Name= 2 formatter = M/d/yy h:mm a
Thread Name= 3 formatter = M/d/yy h:mm a
Thread Name= 6 default Formatter = yyyyMMdd HHmm
Thread Name= 5 formatter = M/d/yy h:mm a
Thread Name= 6 formatter = M/d/yy h:mm a
Thread Name= 7 default Formatter = yyyyMMdd HHmm
Thread Name= 8 default Formatter = yyyyMMdd HHmm
Thread Name= 8 formatter = M/d/yy h:mm a
Thread Name= 7 formatter = M/d/yy h:mm a
Thread Name= 9 default Formatter = yyyyMMdd HHmm
Thread Name= 9 formatter = M/d/yy h:mm a

正如您從輸出中所看到的,Thread-0已經更改了格式化器的值,但是thread-2的默認格式化器仍然與初始化的值相同。其他線程也呈現相同的模式。更新:在Java 8中,ThreadLocal類被擴展,新增了一個名為withInitial()的新方法,它接受Supplier功能介面作為參數。因此,我們可以使用lambda表達式輕鬆地創建ThreadLocal實例。例如,上面的formatter ThreadLocal變量可以定義為以下一行:

private static final ThreadLocal<SimpleDateFormat> formatter = 
	ThreadLocal.<SimpleDateFormat>withInitial
	(() -> {return new SimpleDateFormat("yyyyMMdd HHmm");});

如果您對Java 8的功能不熟悉,請查看Java 8功能Java 8功能介面。這就是關於Java編程中的ThreadLocal的全部內容。參考:API文檔

Source:
https://www.digitalocean.com/community/tutorials/java-threadlocal-example