Java中线程池的作用是什么?

38

线程池的作用是什么?有好的现实世界的例子吗?


http://tutorials.jenkov.com/java-concurrency/thread-pools.html - Channa
7个回答

52

线程池是一组最初被创建的线程,等待任务并执行它们。其思想在于始终保持这些线程的存在,以避免每次创建线程时支付开销时间。当我们知道有一系列要处理的作业时,线程池是合适的选择,即使有时可能没有作业。

以下是来自维基百科的优美图示: alt text


2
我们能否估计所需的线程池数量?或者每个线程池是否有线程数量限制? - Asif Mushtaq

7

线程池是一个准备就绪的工作线程池,它创建Thread并管理它们。线程池会重复使用已经创建的线程(即工作线程),而不是在任务完成后创建线程并丢弃它们。

为什么要用线程池?

因为创建线程是一个耗时的过程,它会延迟请求处理。它还限制了客户端的数量,基于每个JVM允许的线程数,这显然是有限制的。


使用Executor框架创建固定大小的线程池 -

Java 5引入了一个完整功能的内置线程池框架,通常称为Executor框架

使用Java 5 Executor框架创建固定大小的线程池非常容易,因为Executors类提供了静态工厂方法。你所需要做的就是定义要并发执行的任务,然后将该任务提交给ExecutorService

从这里开始,线程池将负责如何执行该任务;它可以由任何空闲的工作线程执行。

public class ThreadPoolExample {
    public static void main(String args[]) {
       ExecutorService service = Executors.newFixedThreadPool(10); //create 10 worker threads in Thread Pool
       for (int i =0; i<100; i++){
           service.submit(new Task(i)); //submit that to be done 
       }
       service.shutdown();
    }  
}

final class Task implements Runnable {
    private int taskId;  
    public Task(int id){
        this.taskId = id;
    }
  
    @Override
    public void run() {
        System.out.println("Task ID : " + this.taskId +" performed by " 
                           + Thread.currentThread().getName());
    }  
}

Output:
Task ID : 0 performed by pool-1-thread-1
Task ID : 3 performed by pool-1-thread-4
Task ID : 2 performed by pool-1-thread-3
Task ID : 1 performed by pool-1-thread-2
Task ID : 5 performed by pool-1-thread-6
Task ID : 4 performed by pool-1-thread-5

*Output may vary from system to system

7

Java教程中的线程池提供了一个很好的概述:

使用工作线程最小化了由于线程创建而产生的开销。线程对象使用大量内存,在大规模应用程序中,分配和释放许多线程对象会产生显著的内存管理开销。


6

谢谢你的建议。希望现在这个答案更加合适了。 - S73417H
不用客气,这已经更好了(之前的评论已被删除)。不过,我建议阅读以下文章:如何处理谷歌问题?在Stack Overflow上询问谷歌可搜索的问题是否不好?,以及在没有进行研究之前,在Stack Overflow上提问是否恰当? - Pascal Thivent
是的...抱歉回答有点不严肃。现在还是早上,咖啡还没起作用。谢谢提供链接。 - S73417H

5

线程池只在服务器-客户端情况下有用,其中无法确定/预测客户端请求的数量/发生次数。

在这种情况下,每次客户端请求都创建一个新线程有两个缺点:

1) 线程创建的运行时延迟: 创建线程需要一定时间,因此实际工作不会在请求进入时立即开始。客户可能会注意到轻微的延迟。

在交互式系统中,这一标准至关重要,客户期望立即得到响应。

2) 未受控制地使用系统资源: 线程消耗系统资源(内存等),因此在出现意想不到的客户请求流时,系统可能会耗尽资源。

线程池通过以下方式解决上述问题:
1) 在服务器启动时创建指定数量的线程,而不是在运行时创建它们。
2) 限制同时运行的线程数。

注意:上述内容适用于固定大小的线程池。


“在这种情况下,您指的是哪种情况?” - Philipp M
1
有客户端向服务器程序提交请求的情况。而且,客户端请求的时间和数量是无法预测的...希望这可以帮到您。 - Avinash Ganta
@AvinashGanta,我们如何估计需要在线程池中创建的线程数? - Asif Mushtaq
我没有一个通用的答案。但是,线程池的大小取决于以下因素: 1)客户端请求的频率: 请求更频繁(需要)在池中使用更多线程。 2)服务器和客户端之间达成的SLA,指定服务器开始处理请求的最长时间: 更紧密的SLA(需要)在池中使用更多线程。 3)可用的系统资源: 系统资源将限制可以在池中创建的最大线程数。 - Avinash Ganta

4
您可以将线程视为实际的工人,将线程池视为工人组。 您可以创建多个组,以满足不同的优先级、目的等需求。因此,一个池可能是用于通用任务的,比如后台调度、电子邮件广播等;而另一个交易处理池则可以同时处理多个事务。在使用Executor Service时,您肯定不希望将事务性作业延迟到其他非关键性活动(如确认电子邮件广播或数据库维护活动)完成之后才完成。您可以将它们分开并独立维护。这是一个非常简单的答案,没有涉及技术术语。

2
已经有很好的答案来解释它,但是让我们通过一个例子来理解它: 没有线程池的问题:考虑一个Web服务器应用程序,每个HTTP请求都由单独的线程处理。如果应用程序仅为每个新的HTTP请求创建一个新线程,并且系统接收到的请求超过了其立即处理的能力,当所有这些线程的开销超过系统容量时,应用程序将突然停止响应所有请求。 使用线程池的解决方案:限制可以创建的线程数,应用程序将不会像它们进来一样快速服务HTTP请求,但它会根据系统的可持续能力快速为它们提供服务。
更多细节(所有线程的开销):为什么创建线程被认为是昂贵的?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接