【Java】三种 ThreadLocal 的简单使用

三种 ThreadLocal 分别是:

  1. jdk 提供的ThreadLocal
  2. jdk 提供的InheritableThreadLocal
  3. 阿里巴巴提供的TransmittableThreadLocal

文章中 demo 的环境代码👇

private ThreadLocal<Object> t = new ThreadLocal<>();
private ThreadLocal<Object> it = new InheritableThreadLocal<>();
private ThreadLocal tt = new TransmittableThreadLocal();

private void printForThreadLocal() {
    System.out.println(Thread.currentThread().getName() + ":" + t.get());
}

private void printForInheritableThreadLocal() {
    System.out.println(Thread.currentThread().getName() + ":" + it.get());
}

private void printForTransmittableThreadLocal() {
    System.out.println(Thread.currentThread().getName() + ":" + tt.get());
}

/**
    * @Description: 创建线程池
    */
private Executor createThreadPool() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(1);
    executor.setMaxPoolSize(10);
    executor.setKeepAliveSeconds(60);
    executor.setQueueCapacity(60);
    executor.setThreadNamePrefix("myThreadPool: ");
    executor.setTaskDecorator(new MyTaskDecorator());
    executor.initialize();
    return executor;
}

ThreadLocal

线程变量,仅供当前线程使用,不存在多线程分享变量问题
缺点:父子线程不可传递

三个方法:

  1. void set(Object value)填充变量
  2. T get()获取变量
  3. void remove()删除变量【当线程结束后,线程变量也会被回收,显示的调用remove并不是必须的操作,但可以加快内存回收的速度】

最简单的 ThreadLocal 用法👇
证明线程之间不共享 ThreadLocal 中的值

@Test
public void test01ThreadLocal() {
    t.set(1);

    new Thread(() -> printForThreadLocal()).start();

    printForThreadLocal();
}

InheritableThreadLocal

是 ThreadLocal 的子类,实现了父子线程共享变量问题
缺点:在使用线程池时,父子线程共享变量仍有问题

实现父子线程共享变量👇

@Test
public void test01InheritableThreadLocal() {
    it.set(1);

    new Thread(() -> printForInheritableThreadLocal()).start();

    printForInheritableThreadLocal();
}

基本数据类型不可证明父子线程指向同一对象👇

@SneakyThrows
@Test
public void test02InheritableThreadLocal() {
    it.set(1);
    printForInheritableThreadLocal();

    new Thread(() -> {
        printForInheritableThreadLocal();
        it.set(2);
        printForInheritableThreadLocal();
    }).start();

    Thread.sleep(500);
    printForInheritableThreadLocal();
}

InheritableThreadLocal中子父线程中指向同一对象👇

@SneakyThrows
@Test
public void test03InheritableThreadLocal() {
    TestDTO testDTO = new TestDTO();
    testDTO.setName("张三");
    testDTO.setAge(20);
    it.set(testDTO);
    printForInheritableThreadLocal();

    new Thread(() -> {
        printForInheritableThreadLocal();
        TestDTO pp = (TestDTO) it.get();
        pp.setName("李四");
        it.set(pp);
        printForInheritableThreadLocal();
    }).start();

    Thread.sleep(500);
    printForInheritableThreadLocal();
}

线程池在init新的线程时,会将主线程的InheritableThreadLocal传递给子线程👇

@SneakyThrows
@Test
public void test04InheritableThreadLocal() {
    it.set(1);
    printForInheritableThreadLocal();

    Executor threadPool = createThreadPool();
    threadPool.execute(() -> printForInheritableThreadLocal());

    printForInheritableThreadLocal();
}

但如果在init子线程时,主线程InheritableThreadLocal中值为null,子线程也是null; 之后主线程被赋了新值,已经被init的子线程再次被调用时,主线程不会赋值给子线程(当前线程池大小为1,则可实现二次调用创建的子线程)👇

@SneakyThrows
@Test
public void test05InheritableThreadLocal() {
    Executor threadPool = createThreadPool();
    threadPool.execute(() -> printForInheritableThreadLocal());

    it.set(1);

    threadPool.execute(() -> printForInheritableThreadLocal());

    printForInheritableThreadLocal();
}

TransmittableThreadLocal

解决了InheritableThreadLocal在线程池时父子线程传递的问题👇

@Test
public void test01TransmittableThreadLocal() {
    Executor threadPool = TtlExecutors.getTtlExecutor(createThreadPool());

    threadPool.execute(() -> printForTransmittableThreadLocal());

    tt.set(1);

    threadPool.execute(() -> printForTransmittableThreadLocal());

    printForTransmittableThreadLocal();
}
posted @ 2023-03-02 11:13  洗衣机o  阅读(58)  评论(0)    收藏  举报