多线程教程 | 第一篇: 线程池(Thread Pool)
1、线程池简介
在开发服务器端软件项目时,软件经常需要处理执行时间很短而数目却非常巨大的请求,如果为每一个请求创建一个新的线程,会导致性能上的瓶颈,因为线程对象的创建和销毁需要JVM频繁地进行处理,如果请求的执行时间很短,可能花在创建和销毁线程对象上的时间真正大于执行任务的时间,若这样,则系统性能大幅降低。
在JDK5中提供了线程池的支持,主要的作用是支持高并发的访问处理,并且可以将线程对象进行复用,核心原理即创建了一个运行效率比较优异的“线程池”,在池中支持线程对象管理,包括创建与销毁,使用池时只需要执行具体的任务即可,线程对象的处理都在池中被封装了。
2、线程池的相关类结构
为了能更清楚的了解线程池相关的内容,我们得把线程池相关的类结构梳理清楚,
首先介绍三个主要的接口,如下所示:
- Executor
- ExecutorService
- ScheduledExecutorService
它们的类图如下:
下面分别介绍这三个接口的具体用法:
2.1 Executor
在介绍线程池之前,不得不提到的一个接口就是java.util.concurrent.Executor,与线程池有关的大部分类都是实现此接口的,该接口的声明如下:
public interface Executor { void execute(Runnable command); }
可以发现该接口的结构非常简单,仅有一个方法,但是Executor是接口,并不能直接使用,所以还得具体的实现类,从上面的类图可以很清楚的看到,它的唯一子实现类为AbstractExecutorService,它的声明如下:
public abstract class AbstractExecutorService implements ExecutorService
它是一个抽象类,所以同样不能实例化,接着往下看,它有一个实现类为ThreadPoolExecutor,它的声明如下:
public class ThreadPoolExecutor extends AbstractExecutorService
它不是抽象类,可以实例化,所以使用该类可以创建类型为Executor的对象。
2.2 ThreadPoolExecutor
这个类不是接口,也不是抽象类,查看源码发现,它有4个构造函数,如下所示:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
看到这里,相信大家已经有点晕了,我猜可能一方面是,构造函数的参数比较多,另外一方面是构造函数也比较多,一定有很多疑问,这也是我学习过程中所遇到的问题,那么我就做一一详细的说明。
问题一:每个参数代表什么意义?
虽然这4个构造函数的参数比较多,但是细心的小伙伴会发现,这4个构造函数有几个参数是一样的,只是最后有几个参数有细微的差别,那么我先来讲解它们相同的部分:
1) corePoolSize(int)
JDK中的描述是这样的:一直在保持在线程池中的线程数量,即使它们都是空闲的,除非设置了allowCoreThreadTimeOut参数。
这句话的意思就是说,这个值指定了线程池中线程的最小数量,也就是一直保持的数量。
2) maximumPoolSize
JDK中的描述是这样的:线程池中允许的最大线程数量。
这句话的意思就是说,这个值指定了线程池中线程的最大数量,也可以看出corePoolSize和maximumPoolSize的关系,一定是maximumPoolSize<corePoolSize,否则直接抛异常。
3) keepAliveTime
JDK中的描述是这样的:当线程池中线程的数量大于corePoolSize,那么超过corePoolSize数量的空闲线程在等待新任务执行的时间如果超过keepAliveTime时间,则超过corePoolSize这部分的线程则会销毁。
4) unit
keepAliveTime的时间单位。
5) workQueue
JDK中的描述是这样的:在任务执行之前,用来存储任务的队列。这个队列仅仅保存通过执行execute方法的Runnable类型的任务。
通过查看源码,可以看清楚的看到这里的参数需要实现BlockingQueue接口,并且队列里面的元素需要是Runnable类型的。
好了,到这里已经介绍完这4个构造函数的公共参数,那么接下来介绍它们不同的部分:
1) threadFactory:ThreadFactory
这个参数的类型为ThreadFactory,通过这个类型的名称就很清楚的知道这是一个创建线程的工厂,那么首先打开这个类看看,可以看到这个类的声明如下:
public interface ThreadFactory { Thread newThread(Runnable r); }
发现这个类居然是一个接口,那么接下来就一定是找它的实现类了,我们发现ThreadPoolExecutor第一个构造函数它的方法体如下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
也就是说,这里传入的ThreadFactory的实现类为defaultThreadFactory,它是Executors下面的一个静态类,实现了ThreadFactory接口,这里不详细介绍ThreadFactory接口,我会在后面的章节里详细介绍。
2) handler:RejectedExecutionHandler
JDK中的描述是这样的:当线程的数量和队列的容量都达到上限时的任务处理策略。
这里有两个点:何为线程的数量达到上限?何为队列的容量达到上限?
线程的数量达到上限也就是说线程池中的数量已经和maximumPoolSize的数量一样了,已经不能再创建额外的线程了。
队列的容量达到上限也就是说我们指定的队列的长度已经达到上限了。比如:我们指定的BlockingQueue的实现类为LinkedBolockingQueue,指定长度为1000,代码如下:
BlockingQueue<String> queue = new LinkedBlockingQueue<>(1000);
这个表示队列的长度为1000。
2.3 Executors
那么问题来了,是不是我们每次创建线程池的时候,都得使用ThreadPoolExecutor去创建,难道没有一个工具类来直接生成一些固定需求的线程池对象吗?答案是否定的。在java.util.concurrent包下的Executors类提供了若干静态方法来生成一些有固定需求的线程池对象。当然这个类下还有其他的一些静态方法,在这里不做说明。那么接下来就详细介绍线程池相关的几个静态方法。
1) newFixedThreadPool,它有两个重载方法,分别如下:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); }
通过上面的对ThreadPoolExecutor的介绍,可以很清楚的了解这个静态方法创建了一个核心线程数和最大线程数一样大小,超时时间为0毫秒,并且队列为LinkedBlockingQueue且无指定队列容量的线程池。
第一个重载方法只需要传入线程的个数即可,第二个重载方法需要传入线程的个数和创建线程的方法。
大家可以思考一下:这个线程池的不足?
PS:它将核心线程的数量和最大线程的数量设置一样大,这样如果任务的数量长期小于线程的数量或者空闲时段比较多,那么在线程池中固定的线程将持续占用资源;另外它并没有指定阻塞队列的长度,如果提交的任务非常多,很容易出现OOM异常。
当然如果没有特殊的需求,这个线程池还是比较常用的。
2) newSingleThreadExecutor,它有两个重载方法,分别如下:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); }
3) newCachedThreadPool,它有两个重载方法,分别如下:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }
相关推荐
### 多线程编程线程池 #### 一、引言 在计算机科学领域,多线程编程是一种广泛采用的技术,旨在通过同时处理多个任务来提高应用程序的性能和响应速度。然而,创建和销毁线程的过程是相对昂贵的,尤其是在处理大量...
本文将深入探讨如何在Winform应用中使用异步多线程和线程池。 一、线程基础 线程是操作系统分配CPU时间的基本单元,每个进程至少包含一个线程。在C#中,可以使用`System.Threading.Thread`类来创建和管理线程。通过...
在C#编程中,多线程和线程池是实现并发执行任务的关键技术,它们能够提升应用程序的性能和响应性。下面将详细讲解这两个概念及其应用。 **一、多线程** 多线程允许程序同时执行多个独立的任务,提高CPU利用率。在...
【多线程 线程池 线程同步--DEMO】 软件平台:Visual Studio 2008 开发语言:C# 引用系统类:System.Threading System.Threading.ThreadPool 模拟多线程以及线程池的使用,对概念理解很有帮助的。
Java 多线程和线程池 Java 多线程是指在 Java 编程语言中,一个线程的动态执行过程。这个过程包括线程的创建、执行和销毁三个阶段。在 Java 中,创建线程有多种方式,包括通过实现 Runnable 接口、继承 Thread 类...
在计算机科学领域,多线程和线程池是并发编程中的关键概念,它们极大地提高了程序的执行效率和系统资源的利用率。线程是操作系统分配CPU时间的基本单位,而线程池则是管理和调度线程的一种机制。 多线程是指在一个...
在IT领域,尤其是在系统编程和并发处理中,C语言多线程编程是一个重要的主题。线程池是一种优化的线程管理技术,它提高了系统资源的利用率,并降低了线程的创建和销毁开销。本文将深入探讨C语言中的线程池及其相关...
在IT行业中,多线程是程序设计中的一个重要概念,特别是在处理并发操作时,如网络服务、数据处理和用户界面更新等。多线程允许程序同时执行...通过源码学习,我们可以深化对多线程和线程池的理解,提升我们的编程能力。
QT多线程(线程池)的使用
本篇文章将详细探讨“常用多线程模板”以及“鱼刺类(Fork/Join框架)多线程线程池”的应用,结合具体的代码实例来帮助理解这些概念。 首先,多线程是指在一个程序中同时执行多个不同的线程,以实现并行处理。在...
最后,尝试自己编写一个简单的应用,例如一个使用epoll的TCP服务器,配合多线程和线程池处理客户端请求,以此巩固所学知识。 总之,epoll、多线程和线程池是构建高性能、高并发系统的关键技术,熟练掌握它们对于...
在多线程或线程池的环境下,我们首先生成一组随机数,然后将这些随机数分配给各个工作线程,每个线程计算其分配到的随机数之和。最后,主线程收集所有工作线程的结果,将它们相加得到最终的一价和。为了确保计算的...
并发编程之线程与线程池.pptx 内部培训PPT 多线程 线程池
多线程与线程池是Java编程中至关重要的概念,特别是在处理高并发场景时,它们的作用尤为突出。本文将深入探讨这两个主题,并结合标签中的"排它锁"、"重入锁"、"共享锁"等概念进行讲解。 首先,线程是操作系统分配...
在IT领域,多线程和线程池是提高系统性能和效率的重要技术,尤其是在处理高并发场景时。本文将深入探讨基于C语言实现这些概念及其相关的知识点。 首先,我们要了解多线程。在单线程程序中,任务是顺序执行的,而多...
在IT行业中,多线程和线程池是高级编程中不可或缺的部分,特别是在视觉计算领域,如CVI(Cooperative Visual Inspection)系统。多线程允许程序同时执行多个任务,提高系统的并发性能,而线程池则是一种管理和优化...
在面试中,关于线程、多线程和线程池的问题可能包括:线程的生命周期、线程的五种状态、死锁的四个必要条件、线程池的工作原理、Java并发包中的工具类如`CountDownLatch`、`CyclicBarrier`、`Semaphore`的用途,以及...
在Java和Android开发中,多线程和线程池是两个关键的概念,它们对于提高程序的执行效率和优化系统资源的使用至关重要。本教程将深入探讨这两个主题,并通过一个名为"ThreadPoolDemo"的示例代码来阐述其核心原理和...
线程池管理和多线程上传是并发编程中的一个重要实践,特别是在大数据传输和网络服务中。在Java等编程语言中,线程池通过有效地管理和复用线程资源,避免了频繁创建和销毁线程带来的开销,提升了系统性能。下面将详细...
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。使用线程池可以有效地控制运行的线程数量,避免过多线程导致系统资源过度消耗,同时也能简化线程的管理和回收。Java的...