
“普通程序员迟早会意识到他们有许多小任务需要不时执行。”
“如果你正在编写游戏,那么它就是各个角色执行的动作。”
“如果你正在编写一个网络服务器,那么它就是来自用户的不同命令:上传照片、将其转码为所需格式、应用所需模板等。”
“迟早,所有大任务都会分解成一组小的、可管理的任务。”
“因此,在这种情况下,一个微妙的问题出现了:你应该如何管理它们?如果你需要在一分钟内执行数百个任务怎么办?为每个任务创建一个线程没有多大意义。Java 机器为每个线程分配相当多的资源。”
“换句话说,创建和销毁线程可能比任务本身花费更多的时间和资源。”
“Java 的创造者想出了一个优雅的解决方案来解决这个问题:ThreadPoolExecutor。
“ ThreadPoolExecutor是一个包含两个东西的类:”
A) 一个任务队列,您可以在程序中出现任务时将其添加到其中。
B)一个线程池,它是一组执行这些任务的线程。
“更重要的是,一旦任务完成,线程就不会被销毁。相反,它们会进入休眠状态,以便在新任务出现时立即准备开始。”
“创建ThreadPoolExecutor时,您可以设置要创建的最大线程数和可以排队的最大任务数。换句话说,您可以将线程数限制为 10,例如,排队任务到 100。”
“这就是ThreadPoolExecutor 的工作原理:”
1) 当你添加一个新任务时,它被放在队列的末尾。
2) 如果队列已满,则抛出异常。
3) 完成一个任务后,每个线程从队列中取出下一个任务并开始执行它。
4)如果队列中没有任务,则线程进入休眠状态,直到有新任务加入。
“我们限制工作线程数量的方法的优势在于,我们拥有的线程越多,它们之间的干扰就越大。拥有 5-10 个工作线程和一长串任务比为大量任务创建 100 个线程,这些线程将相互竞争资源:内存、处理器时间、数据库访问等。”
“这是ThreadPoolExecutor的一个例子:”
ExecutorService service = Executors.newFixedThreadPool(2);
for(int i = 0; i < 10; i++) {
service.submit(new Runnable() { public void run()
{
// Here we download something big from the Internet.
}
});
}
“呃,我没看到……”
“调用 newFixedThreadPool 方法时会创建一个ThreadPoolExecutor对象。”
因此,它非常易于使用。一旦您使用 submit 方法向它添加任务,它就会:
A) 唤醒休眠线程(如果有的话)来执行任务。
B) 如果没有等待线程,则它会为该任务创建一个新线程。
C) 如果达到最大线程数,则它只是将任务放在队列的末尾。
“我特意在示例中加入了 «这里我们从互联网上下载了一些大东西»。如果我们有 100 个任务 «从互联网上下载一些大东西»,那么同时运行很多它们就没有意义了——我们”将受到我们 Internet 连接的带宽限制的阻碍。在这种情况下,几个线程应该足够了。这就是您在上面的示例中看到的:”
ExecutorService service = Executors.newFixedThreadPool(2);
“事实证明,处理一堆任务并没有那么困难。”
“是的。比你想象的更容易。但金会告诉你的。”
GO TO FULL VERSION