关于线程池的线程复用问题

发布于 2022-09-06 11:03:13 字数 2367 浏览 24 评论 0

在这篇博客中https://www.cnblogs.com/sweet...
看到以下观点

package thread.base.threadloacl;

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

/**
 * 
 * @author ZhenWeiLai
 *
 */
public class B {
    static final InheritableThreadLocal<String> threadParam = new InheritableThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        //固定池内只有存活3个线程
        ExecutorService execService = Executors.newFixedThreadPool(3);
        //死循环几次才能看出效果
        while (true) {
            //线程1,里面有两个子线程
            Thread t = new Thread(()->{
                    threadParam.set("abc");
                    System.out.println("t1:" + threadParam.get());
                    Thread t2 = new Thread(()->{
                        System.out.println("t2:" + threadParam.get());
//                        threadParam.remove();
                    });
                    execService.execute(t2);
                    
                    Thread t3 = new Thread(()->{
                        System.out.println("t3:" + threadParam.get());
//                        threadParam.remove();
                });
                    execService.execute(t3);
                    
            });
            execService.execute(t);
            TimeUnit.SECONDS.sleep(1);
            //线程4,线程1同级
            Thread t4 = new Thread(()-> {
                threadParam.set("CBA");
                    System.out.println("t4:" + threadParam.get());
            });
            execService.execute(t4);
        }
    }
}
t1:abc
t2:abc
t3:abc
t4:CBA
t1:abc
t2:abc
t3:abc
t4:CBA
t1:abc
t2:abc
t3:CBA //因复用线程而导致问题
t4:CBA

我想请问,线程复用导致t3输出CBA是怎么样的一个过程?
Ps:在jdk8下已经复现此问题。

2018年1月18日15:04:32 问题补充:
我使用UnSafestaticFieldOffset获取对象的内存地址,四个线程内部分别执行,地址是一致的。
或许是staticFieldOffset这个方法并不是获取内存地址的?

try {
    Field threadParamF = ThreadPool_1.class.getDeclaredField("threadParam");
    System.out.println("t1 threadParam location:" + unsafe.staticFieldOffset(threadParamF));
} catch (NoSuchFieldException e) {
    e.printStackTrace();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

余生再见 2022-09-13 11:03:13
ExecutorService execService = Executors.newFixedThreadPool(3);

这句的意思是有三个线程在同时运行。
t3打印之前那一刻,t4恰好改变了threadParam.set("CBA");,然后,t3打印出来就是t3:CBA

红衣飘飘貌似仙 2022-09-13 11:03:13

ThreadLocal基本上可以理解为以当前Thread为键的Map,执行过t4的Thread再执行t3就会出现这种情况

够钟 2022-09-13 11:03:13

ThreadLocal在线程结束时没有清空,导致在下一次线程运行时读到旧值。

愚人国度 2022-09-13 11:03:13

就是线程的复用,才是t3的线程正好是上次t4的线程,所以取出的threadlocal是同一个。想知道就把每次线程名字或者id输出看看。我本地已经测试,所有非t4的CBA的线程的上一次输出一定是CBA,就是线程复用问题。

三岁铭 2022-09-13 11:03:13

你new的t1 2 3 4不是当做线程使用的,只是执行了他们的run(),真正在运行的是executeService的3个线程,ThreadLocal对象是这3个线程持有的,跟你new的4个没关系

甜是你 2022-09-13 11:03:13

threadlocal 关联的是当前执行线程,jdk中threadlocal的实现只跟线程ID相关,跟线程执行体无关。你的线程池采用的是Executors.newFixedThreadPool(3),意味着你定义的4个线程体的执行会有复用情况,这个时候就会出现你现在所产生的现象。

念三年u 2022-09-13 11:03:13

确实是线程被复用导致的;

code图片描述/code

public class B {
    static final InheritableThreadLocal<String> threadParam = new InheritableThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        //固定池内只有存活3个线程
        ExecutorService execService = Executors.newFixedThreadPool(3);
        //死循环几次才能看出效果
        while (true) {
            //线程1,里面有两个子线程
            Thread t = new Thread(()->{
                threadParam.set("abc");
                System.out.println("["+Thread.currentThread().getName()+"]:"+Thread.currentThread().getId()
                        +" t1:" + threadParam.get());
                Thread t2 = new Thread(()->{
                    System.out.println("["+Thread.currentThread().getName()+"]:"+Thread.currentThread().getId()
                            +" t2:" + threadParam.get());
//                        threadParam.remove();
                });
                execService.execute(t2);

                Thread t3 = new Thread(()->{
                    System.out.println("["+Thread.currentThread().getName()+"]:"+Thread.currentThread().getId()
                            +" t3:" + threadParam.get());
//                        threadParam.remove();
                });
                execService.execute(t3);

            });
            execService.execute(t);
            TimeUnit.SECONDS.sleep(1);
            //线程4,线程1同级
            Thread t4 = new Thread(()-> {
                threadParam.set("CBA");
                System.out.println("["+Thread.currentThread().getName()+"]:"+Thread.currentThread().getId()
                        +" t4:" + threadParam.get());
            });
            execService.execute(t4);
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文