这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战
引言
Java线程的创建非常昂贵,需要JVM和OS(操作系统)配合完成大量的工作:
(1)必须为线程堆栈分配和初始化大量内存块,其中包含至少1MB的栈内存。
(2)需要进行系统调用,以便在OS(操作系统)中创建和注册本地线程。
Java高并发应用频繁创建和销毁线程的操作是非常低效的,而且是不被编程规范所允许的。如何降低Java线程的创建成本?必须使用到线程池。
是什么?线程池是什么?有什么功能?
线程池主要解决了以下两个问题:
(1)提升性能:线程池能独立负责线程的创建、维护和分配。在执行大量异步任务时,可以不需要自己创建线程,而是将任务交给线程池去调度。线程池能尽可能使用空闲的线程去执行异步任务,最大限度地对已经创建的线程进行复用,使得性能提升明显。
(2)线程管理:每个Java线程池会保持一些基本的线程统计信息,例如完成的任务数量、空闲时间等,以便对线程进行有效管理,使得能对所接收到的异步任务进行高效调度。
为什么要用线程池?
在主要大厂的编程规范中,不允许在应用中自行显式地创建线程,线程必须通过线程池提供。由于创建和销毁线程需要时间以及系统资源开销,使用线程池的好处是减少这些开销,解决资源不足的问题。
如何使用线程池?线程池的各个参数详解
使用new ThreadPoolExec()方法创建线程池有四个重载方法:
最全的一共有7个参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
我们从上往下挨个说
1. 核心线程数量
当程序通过execute()/submit()方法提交提交任务时,如果线程池中的有效线程数低于核心线程数,线程池会创建新线程来处理这个任务,直到有效线程数等于核心线程数。 如果此时任务继续提交,不会继续创建线程,而是把任务放到队列中排队。
2. 最大线程数
当排队的队列满了之后,线程池又开始创建新线程,直到活动的线程数等于最大线程数。
3和4. 非核心线程存活时间与时间单位
当线程们开足火力处理完任务后,超过核心线程数量的线程将被销毁,销毁的时间是由存活时间和存活时间单位来确定。
(todo 这一步是如何实现的呢?)
5. 任务队列
线程队列既是我们刚才所说的存放任务的地方。
6. 线程工厂
线程工厂一听就知道是使用了工厂模式的思想,有一个专门的类来创建线程。 默认是使用defaultThreadFactory,当然也可以自定义线程工厂,通过实现ThreadFactory,重写她的newThread()方法,在这里我们可以定义创建出的线程的名称,线程组,优先级和是否守护线程四个参数。
7. 拒绝策略
当我们的工作线程开到最多并且任务队列也被塞满后,这时再提交的线程就会被拒绝,默认的拒绝策略是AbortReject抛出异常,另外还有3种可选的拒绝策略分别是:静默型拒绝,不抛出异常,直接丢弃;丢弃最老的任务;让提交者执行。
实战
说了这么多我们来写一个线程池吧