Java ThreadPoolExecutor:它如何重用线程

Java ThreadPoolExecutor:它如何重用线程,java,multithreading,concurrency,Java,Multithreading,Concurrency,我读到ThreadPoolExecutor有一个线程池,这个池注定要减少创建新线程的成本(至少我这样理解下面的短语): 当您向执行器发送任务时,它会尝试使用池线程 要执行此任务,请避免连续生成 线程。[Java 7并发烹饪书] 但是,据我所知,我们无法在Java中重新启动线程 问题:ThreadPoolExecutor如何避免创建新线程?这很简单-本质上是线程的睡眠,等待被任务唤醒-他们运行该任务,然后再次睡眠 public static void main(final String[] arg

我读到
ThreadPoolExecutor
有一个线程池,这个池注定要减少创建新线程的成本(至少我这样理解下面的短语):

当您向执行器发送任务时,它会尝试使用池线程 要执行此任务,请避免连续生成 线程。[Java 7并发烹饪书]

但是,据我所知,我们无法在Java中重新启动线程


问题:ThreadPoolExecutor如何避免创建新线程?

这很简单-本质上是
线程的睡眠,等待被任务唤醒-他们运行该任务,然后再次睡眠

public static void main(final String[] args) throws Exception {
    final BlockingQueue<Runnable> blockingQueue = new LinkedBlockingDeque<>();
    final Thread t = new Thread(new Runnable() {

        @Override
        public void run() {
            while (true) {
                try {
                    blockingQueue.take().run();
                } catch (InterruptedException ex) {
                    return;
                }
            }
        }
    });
    t.start();
    blockingQueue.add(new Runnable() {

        @Override
        public void run() {
            System.out.println("Task 1");
        }
    });
    blockingQueue.add(new Runnable() {

        @Override
        public void run() {
            System.out.println("Task 2");
        }
    });
}
publicstaticvoidmain(最终字符串[]args)引发异常{
final BlockingQueue BlockingQueue=new LinkedBlockingDeque();
最终线程t=新线程(新可运行(){
@凌驾
公开募捐{
while(true){
试一试{
blockingQueue.take().run();
}捕获(中断异常例外){
返回;
}
}
}
});
t、 start();
blockingQueue.add(新的Runnable(){
@凌驾
公开募捐{
System.out.println(“任务1”);
}
});
blockingQueue.add(新的Runnable(){
@凌驾
公开募捐{
System.out.println(“任务2”);
}
});
}
BlockingQueue
将在
线程为空时阻止该线程。当我添加一个项目时,当前被阻止的
Thread
(s)将被唤醒,其中一个将执行该任务(
LinkedBlockingDeque
是线程安全的)。当
线程
完成任务后,它将返回睡眠状态

for
ThreadPoolExecutor
详细描述了逻辑。
ThreadPoolExecutor
的所有构造函数都采用
BlockingQueue
——这应该会提示您逻辑是如何工作的


注意:这与忙着等待不同。
BlockingQueue
使用
wait
notify
来挂起和唤醒
线程
s,这意味着池中的
线程
s在不处理任务时没有做任何工作。基于忙等待的方法不起作用,因为
线程
s会阻塞所有CPU内核,其轮询不允许程序继续(或至少严重破坏程序)。

Boris提供的信息很好。此外,它们将对worker-run方法的每次调用都围绕一个try-catch-throwable进行包装,这样无论被调用的方法中发生什么错误,线程都不会被扩展。或者其他一些方法来跟踪活动线程并在需要时创建新线程?@tgkprog确实是-提交的代码被包装在一个自定义的
Runnable
中,它不仅处理
Exception
处理,还处理通过
Callable->Future
API执行返回的值。如果你想了解具体的实施细节,最好只看一下。这不是全部。ThreadPoolExecutor可能允许线程死亡,并在其位置构造新线程。请参阅ThreadPoolExecutor文档中的“保持活动时间”。@cabuncutive这是围绕池机制本身的,它与
线程的重用方式无关。@ante.sabo否。线程完全阻塞,因此在
run()
返回之前,无论调用sleep还是阻塞IO,线程都不会返回池。用Java术语来说,您所谈论的是所谓的光纤,很快将包含在JDK中。反应式编程库在这里也有帮助,但您需要非常小心地调用它的sleep方法(通常称为类似于
delay
)而不是将其本身作为睡眠。。。线程完全阻塞。