深入源码探究ThreadLocal本地线程变量

问题引入:在多线程环境下,如何保证自己的变量不被其他线程篡改?

     Spring如何处理Bean在多线程下的线程安全问题?

先看一个线程不安全的例子:

根据不考虑多线程的代码语义可知,我们期望静态变量经每个线程修改后变成该线程的编号并打印。

 1 public class ThreadLocalDemo {
 2     private static int num=0;
 3     public static void main(String[] args) {
 4         Thread[] threads=new Thread[5];
 5         for(int i=0;i<threads.length;i++){
 6             final int count=i;
 7             threads[i]=new Thread(()->{
 8                 num+=count;
 9                 System.out.println(Thread.currentThread().getName()+" :"+num);
10             });
11         }
12         for(int i=0;i<threads.length;i++){
13             threads[i].start();
14         }
15     }
16 }

然而由于多线程环境,实际结果是这样的:

 解决线程安全问题有两大思路:(1)共享,加锁  (2)不共享

  ThreadLocal本地线程变量就是在合适的情形下采用第二种路线来保证线程安全,即将变量私有不共享。使用方法如下:

 1 public class ThreadLocalDemo {
 2     private static int num=0;
 3     private static ThreadLocal<Integer> threadLocal1=new ThreadLocal<Integer>(){
 4         @Override  //ThreadLocal类源码中提供的初始化方法。
 5         protected Integer initialValue() {
 6             return 0;
 7         }
 8     };
 9 
10     public static void main(String[] args) {
11         Thread[] threads = new Thread[5];
12         for (int i = 0; i < threads.length; i++) {
13             final int count = i;
14             threads[i] = new Thread(() -> {
15                 int x = threadLocal1.get();
16                 x += count;
17                 threadLocal1.set(x);
18                 System.out.println(Thread.currentThread().getName() + " :" + threadLocal1.get());
19             });
20         }
21         for (int i = 0; i < threads.length; i++) {
22             threads[i].start();
23         }
24     }
25 }

运行结果:

 那么,ThreadLocal的底层原理是什么呢?ThreadLocal在使用中主要用到两个方法,set()和get(),我们不妨进入set()和get()方法一探究竟。

扫描二维码关注公众号,回复: 9598438 查看本文章

   可见,Thread类中有一个ThreadLocal.ThreadLocalMap类型的变量threadlocals。ThreadLocalMap类是ThreadLocal类中的静态内部类,其中维护了一个Entry类型的数组。Entry类又是ThreadLocalMap类中弱引用的内部类。set()方法赋值时先拿到Thread中的ThreadLocalMap类型变量threadlocals,判断是否为空,如果为空,创建ThreadLocalMap对象赋值给Thread。如果不为空,直接修改value值。get()方法也基本同理。这种设计方式真正意义上实现了线程局部变量的意味。ThreadLocalMap类中维护了Entry类型的数组,表明了一个线程中可以有多个本地线程变量。

  上面大图将get()和Set()方法和几种关键的类指示的非常清晰,看图很容易捋清思路。画了图突然不想写字了。  --_--

  Spring处理Bean在多线程下的线程安全问题就是使用了ThreadLocal本地线程的方法,比如在Service层加事务要求,就要保证每一个Dao层操作的数据库链接必须是同一个(不同的链接就不能保证事务),怎样保证几个操作拿到的数据库链接是同一个呢?就是将数据库连接使用ThreadLocal保存到线程本地变量中。

猜你喜欢

转载自www.cnblogs.com/fangtingfei/p/12420025.html
今日推荐