使用场景:如为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B 线程正在使用的 Connection。还有 Session 管理等问题。
ThreadLocal为每个线程都创建一个map对象。从而保证线程隔离。
- public void set(T value) { // T value , 泛型实现,可以 set 任何对象类型
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
看这个方法,我们能够想到如果是map时,get 的两次结果应该是"Hello World" 和"World is beautiful".但结果却是。。。。
- public class DemoTest{
- public static void main(String[] args){
- ThreadLocal
localVariable = new ThreadLocal<> () ; - localVariable.set("Hello World");
- localVariable.set("World is beautiful");
- System.out.println(localVariable.get());
- System.out.println(localVariable.get());
- }
- }
探究解析:从程序中来看,我们进行了两次 set 方法的使用。
第一次 set 的值为 Hello World ;第二次 set 的值为 World is beautiful。接下来我们进行了两次打印输出 get 方法,那么这两次打印输出的结果都会是 World is beautiful。 原因在于第二次 set 的值覆盖了第一次 set 的值,所以只能 get 到 World is beautiful。
总结:ThreadLocal 中只能设置一个变量值,因为多次 set 变量的值会覆盖前一次 set 的值,我们之前提出过,ThreadLocal 其实是使用 ThreadLocalMap 进行的 value 存储,那么多次设置会覆盖之前的 value,这是 get 方法无需入参的原因,因为只有一个变量值。
- public class DemoTest{
- public static void main(String[] args){
- ThreadLocal
localVariable = new ThreadLocal<> () ; - localVariable.set("Hello World");
- System.out.println(localVariable.get());
- localVariable.remove();
- System.out.println(localVariable.get());
- }
- }
Tips:remove 方法同 get 方法一样,是没有任何入参的,因为 ThreadLocal 中只能存储一个变量值,那么 remove 方法会直接清除这个变量值
上面代码运行的结果,如下图所示。
场景设计:
- package jvm.juc;
-
- public class ThreadLocalTest {
- static ThreadLocal
local = new ThreadLocal<>(); - public static void main(String[] args) {
-
- Thread threadA = new Thread(()->{
- local.set("线程One-在里面设置值了");
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(local.get());
- });
- threadA.setName("ThreadOne");
-
-
- Thread threadB = new Thread(()->{
- local.set("线程Two-在里面设置值了");
- System.out.println(local.get());
- local.remove();
- });
- threadB.setName("ThreadTwo");
-
- threadA.start();
- threadB.start();
- }
- }
从以上结果来看,在 threadTwo 执行完 remove 方法后,threadOne 仍然能够成功打印,这更加证明了 ThreadLocal 的专属特性,线程独有数据,其他线程不可侵犯。
ThreadLocal 是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal 比直接使用 synchronized 同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性,但也有缺点是只能存储一个值。