ThreadLocal问题
ThreadLocal issue
ThreadLocal 向线程提供包装对象的独占副本。
我正在执行一个场景
public class CustomerThread extends Thread{
static Integer custId =0;
private static ThreadLocal t1 = new ThreadLocal(){
protected Integer initialValue() {
return ++custId;
}
};
public CustomerThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+ " executing with customer Id : "+t1.get());
}
}
public class ThreadLocalDemo {
public static void main(String[] args) {
CustomerThread c1 = new CustomerThread("A");
CustomerThread c2 = new CustomerThread("B");
CustomerThread c3 = new CustomerThread("C");
CustomerThread c4 = new CustomerThread("D");
CustomerThread c5 = new CustomerThread("E");
CustomerThread c6 = new CustomerThread("F");
CustomerThread c7 = new CustomerThread("G");
CustomerThread c8 = new CustomerThread("H");
CustomerThread c9 = new CustomerThread("I");
CustomerThread c10 = new CustomerThread("J");
c1.start();
c2.start();
c3.start();
c4.start();
c5.start();
c6.start();
c7.start();
c8.start();
c9.start();
c10.start();
}
}
Threadlocal 应该为每个客户获得唯一的价值,但是当我在上面的场景中运行时,它有时会产生
A 使用客户 ID 执行:1
B 使用客户 ID 执行:1
D 使用客户 ID 执行:3
C 以客户 ID 执行:2
E 以客户 ID 执行:4
F 使用客户 ID 执行:5
G 以客户 ID 执行:6
H 以客户 ID 执行:7
我使用客户 ID 执行:8
J 以客户 ID 执行:9
此处 A 和 B 获得相同的值。
有人可以解释这是正确的行为 w.r.t ThreadLocal 吗?
您的问题与ThreadLocal
无关。你的问题是这不是一个原子操作:
++custID;
可能发生的情况是,线程 A 看到 custID == 1
,将其存储到 ThreadLocal
对象中自己的 bin 中,并分配 custID=2
。同时,线程 B 也看到 custID == 1
并且它做同样的事情。
您需要某种保护——互斥锁或 AtomicInteger
,以确保一次只有一个线程尝试获取新的 custID
值。
P.S.: 我会尽量避免在新代码中使用 ThreadLocal
。 ThreadLocal
的主要用途是将使用 static
变量的旧代码从单线程转换为多线程。但是如果你正在编写新代码,你应该首先尽力避免 static
。 static
使您的代码难以维护,并且 非常 难以正确测试。
在一次性的小程序中使用 static
没有任何问题,只是它会教给你一个坏习惯。
尝试使用 AtomicInteger
作为您的计数器。
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html
ThreadLocal 向线程提供包装对象的独占副本。 我正在执行一个场景
public class CustomerThread extends Thread{
static Integer custId =0;
private static ThreadLocal t1 = new ThreadLocal(){
protected Integer initialValue() {
return ++custId;
}
};
public CustomerThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+ " executing with customer Id : "+t1.get());
}
}
public class ThreadLocalDemo {
public static void main(String[] args) {
CustomerThread c1 = new CustomerThread("A");
CustomerThread c2 = new CustomerThread("B");
CustomerThread c3 = new CustomerThread("C");
CustomerThread c4 = new CustomerThread("D");
CustomerThread c5 = new CustomerThread("E");
CustomerThread c6 = new CustomerThread("F");
CustomerThread c7 = new CustomerThread("G");
CustomerThread c8 = new CustomerThread("H");
CustomerThread c9 = new CustomerThread("I");
CustomerThread c10 = new CustomerThread("J");
c1.start();
c2.start();
c3.start();
c4.start();
c5.start();
c6.start();
c7.start();
c8.start();
c9.start();
c10.start();
}
}
Threadlocal 应该为每个客户获得唯一的价值,但是当我在上面的场景中运行时,它有时会产生
A 使用客户 ID 执行:1
B 使用客户 ID 执行:1
D 使用客户 ID 执行:3
C 以客户 ID 执行:2
E 以客户 ID 执行:4
F 使用客户 ID 执行:5
G 以客户 ID 执行:6
H 以客户 ID 执行:7
我使用客户 ID 执行:8
J 以客户 ID 执行:9
此处 A 和 B 获得相同的值。
有人可以解释这是正确的行为 w.r.t ThreadLocal 吗?
您的问题与ThreadLocal
无关。你的问题是这不是一个原子操作:
++custID;
可能发生的情况是,线程 A 看到 custID == 1
,将其存储到 ThreadLocal
对象中自己的 bin 中,并分配 custID=2
。同时,线程 B 也看到 custID == 1
并且它做同样的事情。
您需要某种保护——互斥锁或 AtomicInteger
,以确保一次只有一个线程尝试获取新的 custID
值。
P.S.: 我会尽量避免在新代码中使用 ThreadLocal
。 ThreadLocal
的主要用途是将使用 static
变量的旧代码从单线程转换为多线程。但是如果你正在编写新代码,你应该首先尽力避免 static
。 static
使您的代码难以维护,并且 非常 难以正确测试。
在一次性的小程序中使用 static
没有任何问题,只是它会教给你一个坏习惯。
尝试使用 AtomicInteger
作为您的计数器。
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html