首先,线程安全主要针对并发情况来说的,在多线程环境下,ThreadLocal 可以提供给各自线程私有的局部变量,使线程间互不影响。
当然,也有例外,比如下面这个栗子:
/**
* @Author Joy
* @Date 2021/8/27
* @Desc ThreadLocal 作用的是同一个对象时,线程不安全,无法隔离
*/
public class ThreadLocal_Unsafe {
private static SingleA singleA = new SingleA();
private static final ThreadLocal<SingleA> threadLocal = new ThreadLocal<SingleA>() {
@Override
protected SingleA initialValue() {
return singleA;
}
};
public static void main(String[] args) {
Thread[] threads = new Thread[5];
for (int i = 0; i < 5; i++) {
threads[i] = new Thread(() -> {
threadLocal.get().setNumber(threadLocal.get().getNumber() + 5);
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get().getNumber());
}, "thread-" + i);
}
for (Thread thr : threads) {
thr.start();
}
}
}
public class SingleA {
private int number = 0;
public int getNumber() {
return number;
}
public void setNumber(int newNumber) {
this.number = newNumber;
}
}
结果:
thread-0: 5
thread-2: 15
thread-1: 10
thread-3: 20
thread-4: 25
上面的代码想让每个线程都打印 5,但是 ThreadLocal 并没有实现变量隔离,为什么呢?
因为多个线程实际上共享了同一个对象,是由 ThreadLocal 初始化时返回的。
正确用法应该是下面这样:
/**
* @Author Joy
* @Date 2021/8/27
* @Desc ThreadLocal 线程安全的用法:初始化返回不同对象,或者基础类型的封装类
*/
public class ThreadLocal_Safe {
private static final ThreadLocal<SingleA> threadLocal = ThreadLocal.withInitial(() -> new SingleA());
public static void main(String[] args) {
Thread[] threads = new Thread[5];
for (int i = 0; i < 5; i++) {
threads[i] = new Thread(() -> {
threadLocal.get().setNumber(threadLocal.get().getNumber() + 5);
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get().getNumber());
}, "thread-" + i);
}
for (Thread thr : threads) {
thr.start();
}
}
/**结果:
* thread-0: 5
* thread-2: 5
* thread-1: 5
* thread-3: 5
* thread-4: 5
*/
}
简单来说,ThreadLocal 是通过创建变量副本的方式,来保证每个线程只能自己玩自己的,破坏了这点就不能保证线程安全了。