使用 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              

您无法在不同的线程中安全地递增 Integer,您应该针对您的情况使用 AtomicIntegergetAndIncrement() 方法。

您的代码不是线程安全的,因为 ++ 运算符不是线程安全的。

你应该使用 AtomicInteger, and there is no reason to use ThreadLocal.

将您的 class 更改为此,以便在创建时分配 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

您的代码有两个问题:

  1. 静态 custId 值的非线程安全递增
  2. 当您每次都创建此 class 的新实例时,无需使用 ThreadLocal 变量(即您的 class 的单个实例不会跨线程共享,而是每个线程共享已经有自己的 class).
  3. 实例

问题 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()和整数自增默认不是线程安全的,所以它们的组合也不会成为线程安全的。

'official' ThreadLocal 示例使用 AtomicInteger(这也是其他人的建议),这使得整数递增线程安全。但是你也可以自由地让你的 initialValue() 方法线程安全,方法是 synchronized:

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

那么您的代码将与简单的 Integer.

一起正常工作