使用 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
,您应该针对您的情况使用 AtomicInteger
和 getAndIncrement()
方法。
您的代码不是线程安全的,因为 ++
运算符不是线程安全的。
你应该使用 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
您的代码有两个问题:
- 静态 custId 值的非线程安全递增
- 当您每次都创建此 class 的新实例时,无需使用 ThreadLocal 变量(即您的 class 的单个实例不会跨线程共享,而是每个线程共享已经有自己的 class).
实例
问题 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
.
一起正常工作
在下面的程序中,我想为每个线程分配不同的 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
,您应该针对您的情况使用 AtomicInteger
和 getAndIncrement()
方法。
您的代码不是线程安全的,因为 ++
运算符不是线程安全的。
你应该使用 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
您的代码有两个问题:
- 静态 custId 值的非线程安全递增
- 当您每次都创建此 class 的新实例时,无需使用 ThreadLocal 变量(即您的 class 的单个实例不会跨线程共享,而是每个线程共享已经有自己的 class). 实例
问题 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
.