一、线程池的优势:二、线程池的架构图:三、Executors工具类中几个重点的创建线程池的方法:线程池的7个重要参数:
一、线程池的优势:
为何要用线程池,总的来说有以下几个原因:
总体来说,线程池有如下的优势:
(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
二、线程池的架构图:interface:接口
两个接口:
第一个接口Executor,它只定义了一个方法execute(Runnable command),相当于定义了一个框架,它能够执行一个任务。Executor就像是定义了一个框架,并确定了这个框架能够提供的功能。
第二个是ExecutorService接口,它继承Executor。主要扩展了关闭线程管理的一些功能,比如shutdown方法用来关闭线程池的任务,isTerminated方法来判断线程池的任务是否结束。
public interface ExecutorService extends Executor //继承了顶级接口Executor
另外ExecutorService还提供了最重要的方法submit,它支持了有返回结果的任务提交,也支持没返回值的任务提交,也引入了实现这个功能最关键的接口Callable、Future;
ExecutorService支持的比较广泛:
submit(Callable task):支持参数Callable,Callable是有返回值的接口
submit(Runnable task):支持参数Runnable ,Runnable是没有返回值的接口
Executor、ExecutorService都是接口(接口只定义方法,不实现方法),只是定义了提交任务、关闭任务等方法,相当于只是申明了线程池支持这些功能。
①:execute(Runnable command):执行行Ruannable类型的任务
②:submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
③:shutdown():温柔的关闭线程池,停止接受新任务,并执行完未完成的任务。
④:shutdownNow():强制关闭线程池,未完成的任务会以列表形式返回!
⑤:isTerminated():返回所有任务是否执行完毕。当调用shutdown()方法后,并且所有提交的任务完成后返回为true;当调用shutdownNow()方法后,成功停止后返回为true;
⑥:isShutdown():返回线程池是否关闭,当调用shutdown()或shutdownNow()方法后返回为true。
AbstractExecutorService抽象类:
public abstract class AbstractExecutorService implements ExecutorService
AbstractExecutorService是线程池系列第三个类,它是抽象类并继承ExecutorService接口。它主要是实现了ExecutorService接口的submit系列方法。
实现submit方法主要依靠RunnableFuture接口和它的实现类FutureTask。RunnableFuture继承Runnable和Future接口,而它的实现FutureTask的一个属性callable是Callable类型。
FutureTask实现了Runnable的run方法,run方法调用的是callable的call方法保存执行结果。同时也实现Future的get方法获取结果,如果任务还没有执行则阻塞线程。
所以submit方法实现的主要过程是提交的任务Callable封装成FutureTask,并把FutureTask当作Runnable丢给execute方法去异步执行,然后把FutureTask当作Future作为submit返回值。
但是AbstractExecutorService并没有实现execute方法,所以它是一个抽象类,在等待有缘人来实现execute,实现线程池的最后一步。
实现类ThreadPoolExecutor:
ThreadPoolExecutor是Executor框架的正真实现者。它实现了execute方法,execute真正的实现,以下三个关键点:
HashSet workers;//Worker集合,线程池
BlockingQueue workQueue;//阻塞队列,要执行的任务
final void runWorker(Worker w);//Worker调用这个方法,可以从阻塞队列中获取任务来执行。
线程池工具类:Executors:
类似于集合和集合工具类
数组和数据的工具类
线程和线程的工具类
Executors中有多个用于创建线程池的方法;
代码演示包含固定数量线程的线程池:
package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo1 {
public static void main(String[] args) {
//使用线程的工具类Executors来创建线程池,固定数量的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
try {
for (int i = 1; i <= 10; i++) {
//execute方法用于执行任务
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+
"t 办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();//关闭线程池
}
}
}
代码演示包含一个线程的线程池:
package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo1 {
public static void main(String[] args) {
//使用线程的工具类Executors来创建线程池,固定数量的线程池
//一池包含5固定数量的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//一池包含一个线程
ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
try {
//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
for (int i = 1; i <= 10; i++) {
//execute方法用于执行任务
threadPool2.execute(()->{
System.out.println(Thread.currentThread().getName()+
"t 办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
threadPool2.shutdown();//关闭线程池2
}
}
}
包含一个线程的线程池打印结果:
代码演示包含N个线程 的线程池:
package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo1 {
public static void main(String[] args) {
//使用线程的工具类Executors来创建线程池,固定数量的线程池
//一池5固定数量的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//一池包含一个线程
ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
//一池包含N个线程,N的数量根据任务量大小动态变化
ExecutorService threadPool3 = Executors.newCachedThreadPool();
try {
//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
for (int i = 1; i <= 10; i++) {
//execute方法用于执行任务
threadPool3.execute(()->{
System.out.println(Thread.currentThread().getName()+
"t 办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
threadPool3.shutdown();//关闭线程池
}
}
}
打印:
或者这样:
package com.fan.threadpool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MyThreadPoolDemo1 {
public static void main(String[] args) {
//使用线程的工具类Executors来创建线程池,固定数量的线程池
//一池5固定数量的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//一池包含一个线程
ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
//一池包含N个线程,N的数量根据任务量大小动态变化
ExecutorService threadPool3 = Executors.newCachedThreadPool();
try {
//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
for (int i = 1; i <= 10; i++) {
//execute方法用于执行任务
threadPool3.execute(()->{
System.out.println(Thread.currentThread().getName()+
"t 办理业务");
});
//MILLISECONDS:/ˈmɪlisekənd/毫秒
try { TimeUnit.MILLISECONDS.sleep(200);}
catch (InterruptedException e) {e.printStackTrace();}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
threadPool3.shutdown();//关闭线程池
}
}
}
打印结果:
创建线程池的方法解读:
从源码中可以看出,线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。下面会对这7个参数一一解释。
一、corePoolSize 线程池核心线程大小:
线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。
二、maximumPoolSize 线程池最大线程数量:
一个任务被提交到线程池以后,首先会找有没有 空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中。
三、keepAliveTime 空闲线程存活时间:(多余/空闲线程数 = 最大线程数 - 核心线程数)
当线程池中的现存线程数量大于corePoolSize的时 候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime才会被销毁,最终会收缩到corePoolSize的大小。
四、unit 空闲线程存活时间单位:
keepAliveTime的计量单位
五、workQueue 工作阻塞队列:
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
①:ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
②:linkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
③:SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 linkedBlockingQuene;
④:priorityBlockingQuene:具有优先级的无界阻塞队列;
六、threadFactory线程工厂。它是ThreadFactory类型的变量,用来创建新线程。默认使用 Executors.defaultThreadFactory() 来创建线程。
七、handler拒绝策略。线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
①:AbortPolicy:直接抛出异常,默认策略;
②:CallerRunsPolicy:用调用者所在的线程来执行任务;
③:DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
④:DiscardPolicy:直接丢弃任务;
线程池监控API:
getActiveCount() :获取线程池中正在执行任务的线程数量
getCompletedTaskCount():获取已执行完毕的任务数
getTaskCount() :获取线程池已执行与未执行的任务总数
getPoolSize():获取线程池当前的线程数
getQueue().size():获取队列中的任务数
线程池工作原理
在创建了线程池后,开始等待请求。
当调用execute()方法添加一个请求任务时,线程池会做出如下判断:
如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
当一个线程完成任务时,它会从队列中取下一个任务来执行。
当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:
如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。



