美文网首页
ThreadLocal

ThreadLocal

作者: 张明学 | 来源:发表于2020-04-19 01:11 被阅读0次

ThreadLocal 是什么?

ThreadLocal并不是一个Thread,而它是Thread的局部变量,可以在当前线程内存储数据,并且这个数据只能在当前线程中可以得到get和set,跟普通变量的不同之处在于每一个线程读取的ThreadLocal变量是对应的互相独立的。主要有这几个方法:initialValue(初始化方法)、get、set、remove方法。

做一个简单的对比测试说明一下

public class ThreadLocalTest {
    // 普通变量
    public Long mGeneral = 0L;
    // ThreadLocal变量
    public ThreadLocal<Long> mThreadLocal = new ThreadLocal() {
        @Override
        protected Object initialValue() {
            log.info("当前线程ID={}初始化initialValue", Thread.currentThread().getId());
            return 0L;
        }
    };
}

测试:

    public static void main(String[] args) {
        log.info("main方法线程ID={}", Thread.currentThread().getId());
        ThreadLocalTest threadLocalTest = new ThreadLocalTest();
        threadLocalTest.mGeneral = 10L;
        threadLocalTest.mThreadLocal.set(100L);

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                log.info("thread1当前线程ID={}", Thread.currentThread().getId());
                log.info("thread1线程中general={}", threadLocalTest.mGeneral);
                log.info("thread1线程中mThreadLocal.get()={}", threadLocalTest.mThreadLocal.get());
            }
        });

        thread1.start();
        log.info("main方法线程中general{}", threadLocalTest.mGeneral);
        log.info("main方法线程中mThreadLocal.get(){}", threadLocalTest.mThreadLocal.get());
    }

提示一下:main会开启一个主线程,主线程中又开启了一个子线程thread1。它们都可以访问threadLocalTest对象。
输出的结果如下:

[    main]main方法线程ID=1
[    main]main方法线程中general10
[Thread-1]thread1当前线程ID=13
[Thread-1]thread1线程中general=10
[    main]main方法线程中mThreadLocal.get()100
[Thread-1]当前线程ID=13初始化initialValue
[Thread-1]thread1线程中mThreadLocal.get()=0

说明:

  1. general在main线程修改之后,在thread1访问的是修改之后的值,general被共个两个线程共享。mThreadLocal在main线程修改之后,在thread1访问时扔会被初始化,mThreadLocal没有被共享。
  2. ThreadLocal在get之前如果没有被set或被remove,那么会先执行initialValue方法,再返回initialValue值。

ThreadLocal提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值。

举个栗子,假如让100个并发累计计数10000次,就是100个线程,每个线程计数100次,如下所示:

Val计数对象,每次计数消耗0.1秒

@Getter
@Setter
public class Val {
    private Long value;

    public void add() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (null == value) {
            value = 0L;
        }
        value = value + 1L;

    }
}
public class ThreadLocalTest2 {

    public static Val val = new Val();

    public static void main(String[] args) throws InterruptedException {
        // 模拟100个并发
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //每个并发计数100次
                    for (int i = 0; i < 100; i++) {
                        val.add();
                    }
                    log.info("线程{}的100次执行完毕", Thread.currentThread().getId());
                }
            }).start();
        }

        // 等60秒,让上面10个线程执行完毕
        Thread.sleep(60 * 1000);
        log.info("val值={}", val.getValue());
    }
}

执行结果每次都不一样,并且小于10000。如[ main]val值=8684。是因为val.value被各个线程共享,在value=value+1的时候,会被别的线程value重置掉。用ThreadLocal 可以解决这个问题。

public class ThreadLocalTest2 {

    public static Set<Val> threadValSet = new HashSet<>();

    public synchronized static void addThreadValSet(Val val) {
        threadValSet.add(val);
    }

    public static ThreadLocal<Val> mThreadLocal = new ThreadLocal() {
        @Override
        protected Val initialValue() {
            Val val = new Val();
            threadValSet.add(val);
            return val;
        }
    };

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //每个并发计数100次
                    Val val = mThreadLocal.get();
                    for (int i = 0; i < 100; i++) {
                        val.add();
                    }
                    log.info("线程{}的100次执行完毕,value={}", Thread.currentThread().getId(), val.getValue());
                }
            }).start();
        }

        // 等60秒,让上面10个线程执行完毕
        Thread.sleep(60 * 1000);
        Long count = 0L;
        for (Val val : threadValSet) {
            count = count + val.getValue();
        }
        log.info("val累计值={}", count);
    }
}

说明一下,上面那种并发也可以在Val的add()方法上加synchronized,但是执行效率在低很多。这里加synchronized只是向Set添加一个值。
结果:[ main]val累计值=10000。每个线程的mThreadLocal里的val都是100。

相关文章

网友评论

      本文标题:ThreadLocal

      本文链接:https://www.haomeiwen.com/subject/vzuxbhtx.html