ThreadLocal线程池使用和TransmittableThreadLocal值传递

一、ThreadLocal配合线程池

注意配合remove方法,线程池是对线程进行复用的,如果没有及时的清理,那么之前对该线程的使用,就会影响到后面的线程了,造成数据不准确。

package com.test.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadLocalAndPool {
	 
    private static ThreadLocal<Integer> variableLocal = ThreadLocal.withInitial(() -> 0);

    public static int get() {
        return variableLocal.get();
    }

    public static void remove() {
        variableLocal.remove();
    }

    public static void increment() {
        variableLocal.set(variableLocal.get() + 1);
    }

    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(2,2,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>(12));

        for(int i=0;i<5;i++){
            executorService.execute(()->{
                long threadId = Thread.currentThread().getId();
                String threadName =  Thread.currentThread().getName();
                int before = get();
                increment();
                int after = get();
                System.out.println("threadid:" + threadId +"  threadName:"+threadName +"  before:" + before + "  after:" + after);
                /* threadlocal与线程池使用的问题了,因为threadlocal维护是 Map<Thread,T>这个结构。
                 * 而线程池是对线程进行复用的,如果没有及时的清理,那么之前对该线程的使用,就会影响到后面的线程了,造成数据不准确。
                 * */
                remove();//增加remove,解决内存泄露    
            });
        }
        executorService.shutdown();
    }
}

注释remove()方法结果
在这里插入图片描述
未注释remove()方法结果
在这里插入图片描述

二、线程池如何传递ThreadLocal

如何解决线程池中的值传递功能?
多个线程之间竞争同一个变量,为了线程安全进行值隔离,可以使用ThreadLocal。

父子线程之间的值传递,可以使用InheritableThreadLocal类来实现。

在遇到线程池等会池化复用线程的执行组件情况下,上述两种方案都会失灵,就需要通过TransmittableThreadLocal类来实现。

采用InheritableThreadLocal实现。阿里开发出来的TransmittableThreadLocal来让线程池提交任务时进行ThreadLocal的值传递。

2.1 maven依赖

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.12.1</version>
</dependency>

2.2 JDK对跨线程传递ThreadLocal的支持

package com.test1;

public class TestThreadLocal {

	public static void main(String[] args) {
		/* 代码块1
		ThreadLocal<Object> threadLocal = new ThreadLocal<>();
		threadLocal.set("father");
		new Thread(() -> {
			System.out.println("subThread:" +threadLocal.get());
		}).start();
		*/
		
		//代码块2
        InheritableThreadLocal<String> itl = new InheritableThreadLocal<>();
        itl.set("father");
        new Thread(()->{
            System.out.println("subThread:" + itl.get());//子线程可以拿到父线程的变量
            itl.set("son");
            System.out.println(itl.get());//子线程修改
        }).start();

        try {
			Thread.sleep(3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}//等待子线程执行完

        System.out.println("thread:" + itl.get()); //子线程修改不影响父线程的变量	
	}
}

执行代码块1,则获取结果为null。执行代码块2结果
在这里插入图片描述

2.3 InheritableThreadLocal的实现原理

InheritableThreadLocal的实现原理,其实特别简单。就是Thread类里面分开记录了ThreadLocal、InheritableThreadLocal的ThreadLocalMap,初始化的时候,会拿到parent.InheritableThreadLocal。直接上代码可以看的很清楚。

class Thread {
	...
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
  
  ...
  
  if (inheritThreadLocals && parent.inheritableThreadLocals != null)
    this.inheritableThreadLocals =
    ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}

JDK的InheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把任务提交给线程池时的ThreadLocal值传递到任务执行时。

2.4 线程池场景下传值

package com.test1;

import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class InheritableThreadLocal1 {
	static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,
            3,
            0,
            TimeUnit.SECONDS,
            new SynchronousQueue<>(),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
 
    public static void main(String[] args) throws InterruptedException {
        InheritableThreadLocal<Object> threadLocal = new InheritableThreadLocal<>();
        threadLocal.set(5);
        for(int i = 0; i < 3; i++){
            threadPoolExecutor.submit(() ->{
            	System.out.println("threadlocal的值:"+threadLocal.get());
            });
           
        }
        threadLocal.remove();
        Thread.sleep(1000);
        threadLocal.set(3);
        for(int i = 0; i < 3; i++){
            threadPoolExecutor.submit(() ->{
            	System.out.println("threadlocal的值:"+threadLocal.get());
            });
        }
        threadLocal.remove();
    }
}

如果不加控制,直接在线程池下传值,数据是失败,结果
在这里插入图片描述
改进版代码如下

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;

public class InheritableThreadLocal2 {
	static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,
            3,
            0,
            TimeUnit.SECONDS,
            new SynchronousQueue<>(),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
 
    public static void main(String[] args) throws InterruptedException {
        ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(threadPoolExecutor);
        InheritableThreadLocal<Object> threadLocal = new TransmittableThreadLocal<>();
        threadLocal.set(5);
        for(int i = 0; i < 3; i++){
            ttlExecutorService.submit(() ->{
            	System.out.println("threadlocal的值:"+threadLocal.get());
            });
        }
        threadLocal.remove();
        Thread.sleep(1000);
        threadLocal.set(3);
        for(int i = 0; i < 3; i++){
            ttlExecutorService.submit(() ->{
            	System.out.println("threadlocal的值:"+threadLocal.get());
            });
        }
        threadLocal.remove();
    }
}

执行结果
在这里插入图片描述
参考文章

https://www.jianshu.com/p/94ba4a918ff5
https://blog.csdn.net/weixin_38336658/article/details/105914390
https://blog.csdn.net/weixin_34080951/article/details/94632393
https://blog.csdn.net/weixin_41751625/article/details/116918284
https://blog.csdn.net/zyhui98/article/details/114459084
https://www.cnblogs.com/wudimanong/p/10457211.html
https://blog.csdn.net/yaomingyang/article/details/122350802

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值