8

Java跨类使用ThreadLocal:线程中的全局变量

 3 years ago
source link: https://last2win.com/2020/09/05/java-threadlocal/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Java跨类使用ThreadLocal:线程中的全局变量 — 浮云的博客

最近由于新增了一个业务场景,导致很多函数的逻辑都需要改变,需要先判断是否属于该业务场景,再做对应的逻辑,原本的打算是在入口函数新增一个变量,然后在每次需要进行逻辑判断时都把flag变量传进去。

但这样做既不优雅,后续如果还要增加业务场景,头都要改大了,如果有一个方法可以传递全局变量,而且仅限于当前线程就好了。于是就想到了ThreadLocal这个之前很少用到的技巧。

以微服务架构为例,dubbo服务提供方在收到调用方的请求后,会把这个请求分配给一个线程进行处理。一般来说,一个请求会一直由同一个线程处理,中间不会切换线程,所以如果有一个线程中共享的变量,可以当全局变量使用。

ThreadLocal实现的就是一个线程中的全局变量,与真正的全局变量的区别在于ThreadLocal的变量是每个线程中的全局变量,也就是说不同线程访问到的值是不一样的。

ThreadLocal最朴素的实现是Map<ThreadId, variable>,每个线程id对应唯一的一个变量。但Java源码并不是Map<ThreadId, variable>的实现。这是因为如果多个线程访问同一个map,这个map需要是线程安全的,构造比较麻烦。Java采用了更简单粗暴的做法:每个线程都有自己的ThreadLocal专属map,里面可以存放多个ThreadLocal变量,这样就解决了多线程同时操作一个map带来的多线程并发问题。

因为要把ThreadLocal的变量当做全局变量使用,需要把变量与初始化函数写在通用的类中,如DDD领域模型中写在Common模块。

具体的实现如下:

public class ThreadLocalContextHolder {
   /**
    * 不同业务设置不同的业务场景,如:业务A设置值为1,业务B设置值为2...
    */
   private static ThreadLocal<Integer> sceneThreadLocal = new ThreadLocal<>();


   public static Integer getScene() {
       return sceneThreadLocal.get();
   }

   public static void initScene(Integer scene) {
       if (ThreadLocalContextHolder.sceneThreadLocal == null) {
           ThreadLocalContextHolder.sceneThreadLocal = new ThreadLocal<>();
       }
       ThreadLocalContextHolder.sceneThreadLocal.set(scene);
   }

   public static void clearScene() {
       initScene(null);
   }

}

说一下我的用法,比如说有业务场景A和B,会调用不同的dubbo接口,我会在dubbo入口处设置好flag变量,接着调用统一的逻辑处理函数。然后在需要对不同业务进行不同处理的场景中,获取ThreadLocal中的变量,进行逻辑判断,这样就减少了变量的传递,代码也就更整洁了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK