最近在找实习,面试中碰到了不少面试官问并发的问题。浅的有问到进程、线程、锁,深的有问到缓存一致性、内存屏障等……
本着只输入不输出能学到的东西始终有限的想法,我决定详细的整理一遍
并发相关的内容,写成一份《并发杂谈》系列文章。不会涉及太多高级语言层面的东西,更多是偏os的底层分享。最后,我选择
ThreadLcoal作为该系列文章的第一篇文章,因为我很久没有写技术类型的博客文章了,而ThreadLocal内容较为简单,正好帮我热热身悉,找一下写技术博客的状态。
总结写在前面
ThreadLocal和锁一样,都是处理并发安全的机制。但是不同于锁,ThreadLocal是一种空间换时间的机制,它在线程本地栈区复制一份堆区的原始数据,后续线程对该数据的操作都会映射到本地栈区的操作,从而避免了多线程对该变量的竞争。
由于ThreadLocal将变量缓存到了栈区且不会同步回堆区,所以线程之间的数据是相互隔离的。如果我们需要线程协作,那ThreadLocal并不适用,还是老老实实用锁吧。
基于这个特性,我们可以通过ThreadLocal实现以下功能:
- 线程之间的数据隔离。
- 不同的线程通过同一个变量访问到不同的数据(如连接不同的数据库、保存不同的会话数据),实现一个类似“多态”的效果。
1. 为什么要 ThreadLocal?
当一个进程无法满足人们对于性能的需求时,人们从进程身上进一步设计了线程。
进程存在的性能问题:当进程中存在某个任务需要调用阻塞的系统调用(如 I/O)时,整个进程会陷入阻塞,影响进程其余模块的正常运行。
进程作为资源分配的基本单位,线程作为 os 调度的基本单位。 站在内存角度,进程拥有一片属于自己的内存,而这片内存又被该进程拥有的若干线程共享着。 线程将这片内存进一步划分,分出了自己私有的栈区和与其他线程共享的堆区。
由于堆区是共享的,线程在并发地对堆区的数据做操作时,很可能干扰到其他线程。 所以我们需要一些机制来帮我们保证线程并发安全。
主流的策略是“锁”。 锁是一个优秀的解决方案,可以完美避免线程并发导致的数据不安全问题。但是,还是那句话,有利就有弊,锁对系统性能有很大的影响。最坏的情况下,并发程序会硬生生变成串行程序。所以锁不能滥用。
而另一种机制就是ThreadLocal,这是一种空间换时间的方法。
2. ThreadLocal具体是怎么做的?
ThreadLocal是一种空间换时间的方法。
如上文所示,线程拥有自己的私有栈区,其他线程无法访问。
ThreadLocal将数据保存在这个栈区,便可以隔离开其他线程,避免数据竞争。
基于这个特性,我们可以通过ThreadLocal实现以下功能:
- 线程之间的数据隔离。
- 不同的线程通过同一个变量访问到不同的数据(如连接不同的数据库、保存不同的会话数据),实现一个类似“多态”的效果。