public class ThreadLocalTest { private static ThreadLocal threadLocal = new ThreadLocal(){ protected Integer initialValue(){ return 0; } }; // 值通报 @Test public void testValue(){ for (int i = 0; i < 5; i++) { new Thread(() -> { Integer temp = threadLocal.get(); threadLocal.set(temp + 5); System.out.println("current thread is " + Thread.currentThread().getName() + " num is " + threadLocal.get()); }, "thread-" + i).start(); } }}以上程序的输出效果是:
current thread is thread-1 num is 5current thread is thread-3 num is 5current thread is thread-0 num is 5current thread is thread-4 num is 5current thread is thread-2 num is 5我们可以看到,每一个线程打印出来的都是5,哪怕我是先通过 ThreadLocal.get()方法获取变量,然后再 set进去,依然不会进行重复叠加。这就是线程隔离。
但是对于引用通报来说,我们又必要多留意一下了,直接上例子看看。
引用通报
public class ThreadLocalTest { static NumIndex numIndex = new NumIndex(); private static ThreadLocal threadLocal1 = new ThreadLocal() { protected NumIndex initialValue() { return numIndex; } }; static class NumIndex { int num = 0; public void increment() { num++; } } // 引用通报 @Test public void testReference() { for (int i = 0; i < 5; i++){ new Thread(() -> { NumIndex index = threadLocal1.get(); index.increment(); threadLocal1.set(index); System.out.println("current thread is " + Thread.currentThread().getName() + " num is " + threadLocal1.get().num); }, "thread-" + i).start(); } }}我们看看运行的效果
current thread is thread-0 num is 2current thread is thread-2 num is 3current thread is thread-1 num is 2current thread is thread-4 num is 5current thread is thread-3 num is 4我们看到值不但没有被隔离,而且还出现了线程安全的问题。
以是我们一定要留意值通报和引用通报的区别,在这里也不讲这两个概念了。
源码分析