ThreadLocal不安全的情况举例(附代码)
ThreadLocal通过Thread.threadlocals保存ThreadLocal的副本,但是ThreadLocal变量在多线程情况下仍然是不安全的。
class MyClass{ private Integer value; public MyClass(){ } public MyClass(Integer value){ this.value=value; } public Integer getValue() { return value; } public void setValue(Integer value) { this.value = value; } @Override public String toString() { return "MyClass{" + "value=" + value + '}'; } } public class ThreadLocalTest { static ThreadLocal<MyClass> threadLocal=new ThreadLocal<>(); public static void main(String[] args) { MyClass myClass=new MyClass(); new Thread(){ @Override public void run() { myClass.setValue(10); threadLocal.set(myClass); System.out.println(currentThread().getName()+":"+threadLocal.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(currentThread().getName()+":"+threadLocal.get()); myClass.setValue(10); //这个值会影响下面线程最后一次打印 } }.start(); new Thread(){ @Override public void run() { myClass.setValue(20); threadLocal.set(myClass); System.out.println(currentThread().getName()+":"+threadLocal.get()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(currentThread().getName()+":"+threadLocal.get()); } }.start(); } }
通过查看上面代码的执行结果,可以看到,两个线程对myClass的value设置后,相互影响,线程逻辑完全混乱了,并不是我们想象中的各自拥有变量副本。
正确的写法是多线程共享ThreadLocal变量,但是不共享ThreadLocal里面的变量。也就是每个线程共享Thread.threadLocals的key,而不不共享Thread.threadLocals的value。
public class ThreadLocalTest111 { static ThreadLocal<MyClass> threadLocal=new ThreadLocal<>(); public static void main(String[] args) { new Thread(){ @Override public void run() { MyClass myClass=new MyClass(10); threadLocal.set(myClass); System.out.println(currentThread().getName()+":"+threadLocal.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(currentThread().getName()+":"+threadLocal.get()); myClass.setValue(10); //这个值会影响下面线程最后一次打印 } }.start(); new Thread(){ @Override public void run() { MyClass myClass=new MyClass(20); threadLocal.set(myClass); System.out.println(currentThread().getName()+":"+threadLocal.get()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(currentThread().getName()+":"+threadLocal.get()); } }.start(); } }
上面这个代码就是线程安全了。