ThreadLocal 是一个用于提供线程局部变量的工具类,它主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景。例如Spring 中的单例Bean在多线程调用的时候,并行计算的时候各个线程缓存自己的中间计算结果等场景。
ThreadLocal 是解决线程安全问题一个很好的思路,它通过为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。在很多情况下,ThreadLocal 比直接使用 synchronized 同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
class PrintRunnable extends Runnable { val number = new ThreadLocal[Double] override def run(): Unit = { number.set(Math.random()) println(number.get()) } } object SimpleApp { def main(args: Array[String]): Unit = { val printRunnable = new PrintRunnable val thread1 = new Thread(printRunnable) val thread2 = new Thread(printRunnable) thread1.start() thread2.start() thread1.join() thread2.join() } }
0.5157676349493098 0.37557496403907353
但是在父子线程要传递一些变量的时候会发现数据不能共享,需要 InheritableThreadLocal 来实现父子线程变量的传递。
class PrintRunnable extends Runnable { val number = new ThreadLocal[Double] override def run(): Unit = { number.set(Math.random()) println(number.get()) val childThread = new Thread(new Runnable { override def run(): Unit = { println(number.get()) } }) childThread.start() childThread.join() } } object SimpleApp { def main(args: Array[String]): Unit = { val printRunnable = new PrintRunnable val thread1 = new Thread(printRunnable) val thread2 = new Thread(printRunnable) thread1.start() thread2.start() thread1.join() thread2.join() } }
0.5475226099407153 0.8376546404552231 null null
正确的做法:
class PrintRunnable extends Runnable { val number = new InheritableThreadLocal[Double] override def run(): Unit = { number.set(Math.random()) println(number.get()) val childThread = new Thread(new Runnable { override def run(): Unit = { println(number.get()) } }) childThread.start() childThread.join() } } object SimpleApp { def main(args: Array[String]): Unit = { val printRunnable = new PrintRunnable val thread1 = new Thread(printRunnable) val thread2 = new Thread(printRunnable) thread1.start() thread2.start() thread1.join() thread2.join() } }
0.006425375134899158 0.021932306310074368 0.006425375134899158 0.021932306310074368
在子线程中不能修改父线程的变量:
class PrintRunnable extends Runnable { val number = new InheritableThreadLocal[Double] override def run(): Unit = { number.set(Math.random()) println(number.get()) val childThread = new Thread(new Runnable { override def run(): Unit = { println(number.get()) number.set(0.1) } }) childThread.start() childThread.join() println(number.get()) } } object SimpleApp { def main(args: Array[String]): Unit = { val printRunnable = new PrintRunnable val thread1 = new Thread(printRunnable) thread1.start() thread1.join() } }
0.7413853012849937 0.7413853012849937 0.7413853012849937
但是现实开发中我们更多的是使用线程池管理线程,线程复用怎么解决ThreadLocal变量的传递或者跨线程池传递。如下面场景:分布式跟踪系统,日志收集记录系统上下文,Session级Cache,应用容器或上层框架跨应用代码给下层SDK传递信息。
可以看一下这个开源项目transmittable-thread-local