我想问一下Zeller一年多前发布的同一个问题的更多细节......
javadoc表示Executors.newCachedThreadPool
返回的服务重用了线程。这怎么可能?
我知道如何在内部设置队列结构,我不知道它是如何重用队列中的线程的。
我见过的所有示例都让开发人员创建了一个线程实例并通过"执行"方法
例如......
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread(i); //will create 10 instances
executor.execute(worker);
}
我理解线程池可以轻松管理每个线程的生命周期,但同样,我看不到任何方法,也没有能力访问或重新启动池中的任何线程。
在上面的例子中,我希望每个线程都可以由线程池启动,运行,终止和处理,但永远不会重复使用。
消息传递系统将是您需要的地方的一个示例。假设你有一个onMessage处理程序,并且你想重用池中的一个线程来处理它,所以我希望像...这样的方法...
worker = executor.getIdleThread;
worker.setData(message);
executor.resubmit(worker);
或者让ExecutorService充当工厂类并让它返回一个线程实例,在内部它决定创建一个新实例或重用旧实例。
ExecutorService executor = Executors.newCachedThreadPool(WorkerThread);
Runnable worker = executor.getThread;
worker.setData(message);
所以我错过了一些东西。这可能是一件简单的事情,但是我在下午阅读了教程和示例,但仍然没有想到它。有人可以对这个问题有所了解吗?
答案 0 :(得分:2)
我了解线程池可以轻松管理生命周期 每个线程,但再次,我看到没有方法,也没有访问或 重启池中的任何线程。
线程的管理是在内部完成的。 ExecutorService
界面仅提供外部可见的方法。
javadoc of newCachedThreadPool
只是陈述
创建一个线程池,根据需要创建新线程,但是会 在先前构造的线程可用时重用它们。 [...] 要执行的调用将重用 先前构造的线程(如果可用)。如果没有现有线程 可用时,将创建一个新线程并将其添加到池中。主题 未使用60秒的任务将被终止并删除 从缓存。因此,一个长时间闲置的游泳池将会 不消耗任何资源。 [...]
这就是你得到的保证。如果您想知道它是如何实现的,您可以查看源代码,特别是ThreadPoolExecutor
的代码。基本上,空闲线程将在一段时间后不执行任务后终止。
答案 1 :(得分:2)
我很好奇这是怎么可能的,因为线程无法重启,所以我分析了ThreadPoolExecutor
的代码,这是你通过静态获得的所有ThreadPool ExecutorService
的实现构造
首先,正如在另一个答案中所述,你不使用Threads,但在ThreadPools中使用Runnables,因为这会破坏目的。所以这里详细解释了ExecutorService如何重用Threads:
您通常会在submit()
内添加一个Runnable,内部调用execute()
方法。基本上,这会将runnable添加到队列中,如果没有工作ATM,则添加一个Worker
public void execute(Runnable command) {
...
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reje
ct(command);
}
执行程序维护一堆Worker(内部类ThreadPoolExecutor
)。它有你提交的runnable和一个Thread,它将通过你可能设置的ThreadFactory创建,或者只是一个默认的; Worker本身也是一个Runnable,用于从工厂创建Thread
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
...
Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
...
}
添加工人时,它会被启动
private boolean addWorker(Runnable firstTask, boolean core) {
...
Worker w = new Worker(firstTask);
Thread t = w.thread;
...
t.start();
...
return true;
}
runWorker()
方法在循环中运行并使用getTask()
获取您提交的可在workingQueue
中排队的runnables,并在getTask unitl等待超时发生。
final void runWorker(Worker w) {
Runnable task = w.firstTask;
w.firstTask = null;
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
clearInterruptsForTaskRun();
try {
beforeExecute(w.thread, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
...
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
这是getTask()方法
private Runnable getTask() {
...
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
..
} catch (InterruptedException retry) {
...
}
}
}
tl; dr 所以基本上Threadpool维护在循环中运行的工作线程并执行阻塞队列给出的runnables。工作人员将因需求而被创建和销毁(不再执行任务,工作人员将结束;如果没有自由工作人员,则&lt; maxPoolSize然后创建新工作人员)。我也不会称它为“#34;重复使用&#34;更多的线程将被用作执行所有runnables的looper。