验证ThreadLocal的线程安全性

ThreadLocal线程安全吗,先说结论:ThreadLocal线程安全。说实话我之前虽然觉得是,但是也不确定,但是今天通过实验证明是线程安全的。

@RestController
public class ThreadLocalController {
    private final ThreadLocal<String> threadLocal = new ThreadLocal<>();

    @RequestMapping("/test")
    public String test(@RequestParam String value){
        String s = threadLocal.get();
        System.out.println(Thread.currentThread().getName()+"___"+s);
        if (s==null){
            threadLocal.set(value);
            s=threadLocal.get();
        }
        try {
            //为了避免线程执行完值被移除的可能,睡眠10秒,确保执行下一次请求时,上一个线程还是活的
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return s;
    }
}

实验如下:
短时间内发起3次请求,确保发起请求时,前面的线程还没执行完。请求示例如下:
http://localhost:8090/test?value=zhangsan1
http://localhost:8090/test?value=zhangsan2
http://localhost:8090/test?value=zhangsan3
实验结果:
控制台:
http-nio-8090-exec-4___null
http-nio-8090-exec-5___null
http-nio-8090-exec-6___null
浏览器:
zhangsan1
zhangsan2
zhangsan3
实验结论:
ThreadLocal线程安全。

实验方式二:

	private static ThreadLocal threadLocal= new ThreadLocal();
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,10,TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000));
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            int j = i;
            executor.execute(()->{
                Object o = threadLocal.get();
                if (o==null){
                    threadLocal.set(j);
                    o = threadLocal.get();
                }
                System.out.println("o = " + o);
            });
        }
    }

实验结果如下:
o = 6
o = 9
o = 4
o = 3
o = 0
o = 5
o = 2
o = 7
o = 8
o = 1
实验结论:
ThreadLocal线程安全。
但是实验方式二有点小问题,比如循环次数改为20

	private static ThreadLocal threadLocal= new ThreadLocal();
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,10,TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000));
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            int j = i;
            executor.execute(()->{
                Object o = threadLocal.get();
                if (o==null){
                    threadLocal.set(j);
                    o = threadLocal.get();
                }
                System.out.println("o = " + o);
            });
        }
    }

实验结果如下:
o = 1
o = 1
o = 1
o = 1
o = 1
o = 1
o = 1
o = 1
o = 1
o = 1
o = 1
o = 0
o = 5
o = 6
o = 2
o = 8
o = 3
o = 7
o = 9
o = 4
出现了重复的情况,这是因为线程池的线程是重复利用的,解决方法如下:

	private static ThreadLocal threadLocal= new ThreadLocal();
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,10,TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000));
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            int j = i;
            executor.execute(()->{
                try {
                    Object o = threadLocal.get();
                    if (o==null){
                        threadLocal.set(j);
                        o = threadLocal.get();
                    }
                    System.out.println("o = " + o);
                } finally {
                    //每次执行完把ThreadLocal里的值移除
                    threadLocal.remove();
                }
            });
        }
    }

为什么同样是线程池,前面不会重复呢?
因为线程池的线程小于核心线程数的时候,每次使用线程池执行任务都会创建一个核心线程,即使这时候有些核心线程是空闲状态,直至线程池的线程数等于核心线程数。所以前面循环10次,核心线程数也为10,刚好就没次执行都会创建一个新的线程而不会重复。
所以线程池使用ThreadLocal时必须确保每次用完都要移除掉ThreadLocal的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值