线程封闭
当访问共享变量时,往往需要加锁来保证数据同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程中访问数据,就不需要同步了,这种技术称为线程封闭。
线程封闭技术有一个常见的应用,JDBC的Connection对象。Connection对象在实现的时候并没有对线程安全做太多的处理,JDBC的规范里也没有要求Connection对象必须是线程安全的。实际在服务器应用程序中,线程从连接池获取了一个Connection对象,使用完再把Connection对象返回给连接池,由于大多数请求都是由单线程采用同步的方式来处理的,并且在Connection对象返回之前,连接池不会将它分配给其他线程。因此这种连接管理模式处理请求时隐含的将Connection对象封闭在线程里面,这样我们使用的Connection对象虽然本身不是线程安全的,但是它通过线程封闭也做到了线程安全。
实现线程封闭,一般有三种方法:Ad-hoc 线程封闭、堆栈封闭、ThreadLocal线程封闭。
ThreadLocal
ThreadLocal 是JDK 包提供的,它提供了线程本地变量,也就是如果你创建了一个ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。创建一个ThreadLocal 变量后,每个线程都会复制一个变量到自己的本地内存中。
1 | // 一个简单ThreadLocal示例 |

实现原理

Thread 类中有一个threadLocals 和一个inheritableThreadLocals , 它们都是ThreadLocalMap 类型的变量, 而ThreadLocalMap 是一个定制化的Hashmap 。其实每个线程的本地变量不是存放在ThreadLocal 实例里面,而是存放在调用线程的threadLocals 变量里面。也就是说, ThreadLocal 类型的本地变量存放在具体的线程内存空间中。而ThreadLocal 就是一个工具壳类,它通过set 方法把value 值放入调用线程的threadLocals 里面并存放起来,当调用线程调用它的get 方法时,再从当前线程的threadLocals 变量里面将其拿出来使用。
ThreadLocal工具类的set方法:
1 | public void set(T value) { |
ThreadLocal工具类的get方法:
1 | public T get() { |
其他
内存泄露与预防:每个线程的本地变量存放在线程自己的内存变量
threadLocals中,如果当前线程一直不消亡, 那么这些本地变量会一直存在, 所以可能会造成内存溢出, 因此使用完毕后要记得调用ThreadLocal的remove方法删除对应线程的threadLocals中的本地变量。InheritableThreadLocal:InheritableThreadLocal继承自ThreadLocal, 其提供了一个特性,就是让子线程可以访问在父线程中设置的本地变量。它的实现原理是当父线程创建子线程时,构造函数会把父线程中inheritableThreadLocals变量里面的本地变量复制一份保存到子线程的inheritableThreadLocals变量里面。