ThreadLocal 线程不安全问题分析

   我们知道ThreaLocal是线程的副本,每个线程都持有各自的ThreadLocal副本,互不干扰,那么又怎么会有线程安全问题呢?

一. 先来一个实验案例

/**
 * @author charles
 * @createTime 2020/6/7 14:39
 * @description threadLocal 可能产生的线程不安全因素测试
 */
public class ThreadLocalUnSafe extends Thread{
    private static Company company = new Company(0);
    private static ThreadLocal<Company> threadLocal = new ThreadLocal<Company>();
    @Override
    public void run() {
        // 每个线程都进行 + 1 操作
        company.setAge(company.getAge() + 1);
        // 存储到ThreadLocal中
        threadLocal.set(company);
        try {
            // 休眠可以使结果差异更容易看到
            Thread.sleep(1);
        } catch (InterruptedException e) {
            //todo
        }
        System.out.println(Thread.currentThread().getName() + "---" + threadLocal.get().getAge());
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new ThreadLocalUnSafe().start();
        }
    }
    
    static class Company{
        private int age;
        public Company(int i) {
        }
        public Company() {
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }

结果:
ThreadLocal 线程不安全问题分析_第1张图片
ThreadLocal 线程不安全问题分析_第2张图片

二. 结果分析

   这个测试程序启用了5个子线程,本意是各个线程操作各自的ThreadLocal中持有的Company。但是,显然这个结果与我们的初衷相悖。可是这又是为什么呢?ThreadLocal不是各自持有自己的变量副本吗?
   这个原因在于我们使用的new Company(0) 是全局的静态变量,全局共享的。而当我们查看ThreadLocalMap时,不难发现,其存储的是一个对象的引用。当线程休眠时,一个线程改变了这个共享变量company时,也将会影响到其他线程持有的此变量,因此会出现线程不安全的问题。
   如果我们使用如下方法修改ThreadLocal初始化,重新new一个对象呢?

private static ThreadLocal<Company> threadLocal = new ThreadLocal<Company>(){
    @Override
    protected Company initialValue() {
        return new Company();
    }
};

   测试结果:
ThreadLocal 线程不安全问题分析_第3张图片
   重新new了对象也是不行,继续寻找原因:ThreadLocal在初始化的时候调用的initialValue()方法时,初始化了一个null,当调用get方法时,才会调用initialValue()方法初始化值。而threadLocal调用的set()方法set进去的对象时全局变量company,因此此方法显然无效。
   实际上我们只需要去掉new Company(0)的static关键字修饰即可,这样每个ThreadLocal都是持有各自的Company。就不会出现线程安全问题了。
ThreadLocal 线程不安全问题分析_第4张图片

你可能感兴趣的:(java,多线程并发编程)