簡體   English   中英

使用ThreadLocal為每個線程分配ID

[英]Assign ID to each thread using ThreadLocal

在下面的程序中,我想為每個線程分配唯一的ID,但是在輸出中,每個線程的ID不一致,如輸出所示。 但是,如果我取消注釋system.out語句,則會為每個線程分配唯一的ID,不確定原因。

class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException, 
ExecutionException {
    CustomerThread custThread1 = new CustomerThread("Sampath");
    CustomerThread custThread2 = new CustomerThread("Harish");
    CustomerThread custThread3 = new CustomerThread("Harsha");
    CustomerThread custThread4 = new CustomerThread("Gowtham");
    custThread1.start();
    custThread2.start();
    custThread3.start();
    custThread4.start();
    }
}

class CustomerThread extends Thread {
static Integer custId = 0;
private  static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
    @Override
    protected Integer initialValue() {
        //System.out.println("will work");
        return ++custId;
    }
};

CustomerThread(String name) {
    super(name);
}

public void run() {
    System.out.println(Thread.currentThread().getName() + " executing with id: " + tl.get());
}
}

輸出為:

Sampath executing with id: 1
Harish executing with id: 
Harsha executing with id: 2
Gowtham executing with id: 1

預期的輸出是具有唯一ID的線程:

Sampath executing with id: 1
Harish executing with id: 2
Harsha executing with id: 3
Gowtham executing with id: 4              

您的代碼不是線程安全的,因為++運算符不是線程安全的。

您應該使用AtomicInteger ,沒有理由使用ThreadLocal

將您的類更改為此,以在創建時分配ID,即按創建順序分配ID,直到首次使用時才推遲:

class CustomerThread extends Thread {
    private static final AtomicInteger prevCustId = new AtomicInteger();
    private final int custId;

    CustomerThread(String name) {
        super(name);
        this.custId = prevCustId.incrementAndGet();
    }

    @Override
    public void run() {
        System.out.println(getName() + " executing with id: " + this.custId);
    }
}

樣本輸出

Sampath executing with id: 1
Harsha executing with id: 3
Gowtham executing with id: 4
Harish executing with id: 2

您無法在不同的線程中安全地遞增Integer ,因此應為您的案例使用AtomicIntegergetAndIncrement()方法。

您的代碼有兩個問題:

  1. 非線程安全增加您的靜態custId值
  2. 每次已經在創建此類的新實例時都無需使用ThreadLocal變量(即,該類的單個實例不會在線程之間共享,而是每個線程已經具有自己的類實例)。

問題1的解決方法是使用AtomicInteger或在同步塊內執行增量操作。

問題2的解決方法是簡單地刪除靜態ThreadLocal變量,而僅使用常規的非靜態變量。

代碼的固定版本:

public class ThreadLocalDemo
{

    public static void main(String[] args) throws InterruptedException {
        CustomerThread custThread1 = new CustomerThread("Sampath");
        CustomerThread custThread2 = new CustomerThread("Harish");
        CustomerThread custThread3 = new CustomerThread("Harsha");
        CustomerThread custThread4 = new CustomerThread("Gowtham");
        custThread1.start();
        custThread2.start();
        custThread3.start();
        custThread4.start();
    }
}

class CustomerThread extends Thread {
    static AtomicInteger custId = new AtomicInteger(0);
    private int tl;

    CustomerThread(String name) {
        super(name);
        tl = custId.incrementAndGet();
    }

    public void run() {
        System.out.println(Thread.currentThread().getName() + " executing with id: " + tl);
    }

}

您在此處看到的是,默認情況下, ThreadLocal.initialValue()和整數增量不是線程安全的,因此它們的組合也不會變為線程安全的。

“官方” ThreadLocal示例使用AtomicInteger(這也是其他人的建議),這使得整數增量線程安全。 但是您也可以通過使其synchronized來自由地使initialValue()方法成為線程安全的:

// ... your original code ...
    synchronized protected Integer initialValue() {
// ... your original code ...

然后,您的代碼將可以與簡單的Integer一起正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM