Tch
Tch
Published on 2022-09-17 / 34 Visits
0
0

多线程状态下使用ThreadLocal对全局变量进行数据隔离

1. 提出问题

在工作中有这样一个场景,多个方法间调用的时候有大量的参数,功能上也没问题。但是在可读性和美观上还是不够完美,尤其是多个方法都有相同的参数。所以为了近一步优化,可以将这些参数抽出来作为全局参数,值发生变化时对全局变量重新赋值就行了。但是这样做又产生了另一个问题,先看一个栗子。

public abstract class BaseConntroller {

    protected int id = 1;

    public abstract void test(int id);
}

可以看出这是一个抽象类,一个全局变量id初始值是1,一个抽象方法test()

@RestController
@Slf4j
public class DemoController extends BaseConntroller{

    @Override
    @GetMapping("test")
    public void test(@RequestParam("id") int id) {
        log.info("线程 = " + Thread.currentThread().getName());
        log.info("id的值 修改前 = " + this.id);
        this.id = id;
        log.info("id的值 修改后 = " + this.id);
    }
}

这是一个子类继承上面的抽象类(也可以是这个类的全局变量,此栗子是我工作中遇到的真实场景),通过一个GET请求修改全局变量id的值。

现在发出请求

2022-09-17 21:45:03.726  INFO 8172 --- [nio-8080-exec-3] xyz.mytch.controller.DemoController      : 线程 = http-nio-8080-exec-3
2022-09-17 21:45:03.726  INFO 8172 --- [nio-8080-exec-3] xyz.mytch.controller.DemoController      : id的值 修改前 = 1
2022-09-17 21:45:03.726  INFO 8172 --- [nio-8080-exec-3] xyz.mytch.controller.DemoController      : id的值 修改后 = 2

2022-09-17 21:45:54.998  INFO 8172 --- [nio-8080-exec-5] xyz.mytch.controller.DemoController      : 线程 = http-nio-8080-exec-5
2022-09-17 21:45:54.998  INFO 8172 --- [nio-8080-exec-5] xyz.mytch.controller.DemoController      : id的值 修改前 = 2
2022-09-17 21:45:54.998  INFO 8172 --- [nio-8080-exec-5] xyz.mytch.controller.DemoController      : id的值 修改后 = 3

两次请求间服务没有重新启动,可以看出多线程操作同一个全局变量会出现干扰情况。

2.解决问题

一个线程操作的时候另一个线程修改了全局变量,出现这种情况肯定是不行的。此时就可以用到一个好东西ThreadLocal对全局变量进行隔离,以实现我的目的。对上面的栗子做个小小的修改

public abstract class BaseConntroller {

    // 这个可以不用设置初始值,只是为了便于看到修改前后的变化
    protected ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };

    //protected int id = 1;

    public abstract void test(int id);
}
@RestController
@Slf4j
public class DemoController extends BaseConntroller{

    @Override
    @GetMapping("test")
    public void test(@RequestParam("id") int id) {
        log.info("线程 = " + Thread.currentThread().getName());
        log.info("id的值 修改前 = " + threadLocal.get());
        threadLocal.set(id);
        log.info("id的值 修改后 = " + threadLocal.get());
    }
}

下来又是两个请求,看看效果。

4edb62edc2b3bbfd7808adad338b6b70.png

2022-09-17 22:01:34.764  INFO 15880 --- [nio-8080-exec-5] xyz.mytch.controller.DemoController      : 线程 = http-nio-8080-exec-5
2022-09-17 22:01:34.764  INFO 15880 --- [nio-8080-exec-5] xyz.mytch.controller.DemoController      : id的值 修改前 = 1
2022-09-17 22:01:34.764  INFO 15880 --- [nio-8080-exec-5] xyz.mytch.controller.DemoController      : id的值 修改后 = 2

822534f8e44cb8cc6be779deaffd8cc7.png

2022-09-17 22:02:13.637  INFO 15880 --- [nio-8080-exec-7] xyz.mytch.controller.DemoController      : 线程 = http-nio-8080-exec-7
2022-09-17 22:02:13.637  INFO 15880 --- [nio-8080-exec-7] xyz.mytch.controller.DemoController      : id的值 修改前 = 1
2022-09-17 22:02:13.637  INFO 15880 --- [nio-8080-exec-7] xyz.mytch.controller.DemoController      : id的值 修改后 = 3

可以看出来吧,问题完美解决。


Comment