概念
当ThreadLocal里面存储的是static修饰的类或者变量也会出现线程不安全问题,因为在ThreadLocal里面持有的是对象的本身的引用,对于static变量而言,所有的类共享了static修饰的类或者变量的引用.
在ThreadLocal存放的是同一个对象的引用,所以多个线程看到的都是同一个对象的实例.所以ThreadLocal对static修饰的类或者变量不具备线程隔离.
解决办法
把static变量去掉,让它在每个线程都是独立的(在每个线程都单独new一个实例给ThreadLocal,这样就不会共享了.)
代码演示
static修饰了变量
import utils.SleepTools;
/**
* 类说明:ThreadLocal的线程不安全演示
* 多执行几次方法,看控制台输出,十个线程输出结果不全是 10 而且也没有做到线程隔离,这就是线程不安全的问题.
*/
public class ThreadLocalUnsafe implements Runnable {
//因为是static的,就被几个线程共享了,所以就产生了线程不安全的情况
public static Number number = new Number(0);
//把static去掉就正确了,让每个线程都拥有自己独立的number.此时十个线程的输出都是1
// public Number number = new Number(0);
public void run() {
//每个线程计数加一
number.setNum(number.getNum() + 1);
//将其存储到ThreadLocal中
value.set(number);
SleepTools.ms(2);
//输出num值
System.out.println(Thread.currentThread().getName() + "=" + value.get().getNum());
}
public static ThreadLocal<Number> value = new ThreadLocal<Number>() {
};
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new ThreadLocalUnsafe()).start();
}
}
private static class Number {
public Number(int num) {
this.num = num;
}
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String toString() {
return "Number [num=" + num + "]";
}
}
}
控制台打印结果
Thread-9=10
Thread-0=10
Thread-2=10
Thread-8=10
Thread-4=10
Thread-7=10
Thread-6=10
Thread-3=10
Thread-1=10
Thread-5=10
十个线程共享了这一个变量, 说明出现了线程安全问题
给static去掉
import utils.SleepTools;
/**
* 类说明:ThreadLocal的线程不安全演示
* 多执行几次方法,看控制台输出,十个线程输出结果不全是 10 而且也没有做到线程隔离,这就是线程不安全的问题.
*/
public class ThreadLocalUnsafe implements Runnable {
//因为是static的,就被几个线程共享了,所以就产生了线程不安全的情况
// public static Number number = new Number(0);
//把static去掉就正确了,让每个线程都拥有自己独立的number.此时十个线程的输出都是1
public Number number = new Number(0);
public void run() {
//每个线程计数加一
number.setNum(number.getNum() + 1);
//将其存储到ThreadLocal中
value.set(number);
SleepTools.ms(2);
//输出num值
System.out.println(Thread.currentThread().getName() + "=" + value.get().getNum());
}
public static ThreadLocal<Number> value = new ThreadLocal<Number>() {
};
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new ThreadLocalUnsafe()).start();
}
}
private static class Number {
public Number(int num) {
this.num = num;
}
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String toString() {
return "Number [num=" + num + "]";
}
}
}
控制台输出结果
Thread-9=1
Thread-3=1
Thread-8=1
Thread-2=1
Thread-4=1
Thread-0=1
Thread-1=1
Thread-7=1
Thread-5=1
Thread-6=1
可以看到10个线程都是用自己线程里面的变量,没有线程共享一个变量.