线程池实现原理
当向线程池提交一个任务之后,线程池的处理流程如下:
1、判断是否达到核心线程数,若未达到,则直接创建新的线程处理当前传入的任务,否则进入下个流程
2、线程池中的工作队列是否已满,若未满,则将任务丢入工作队列中先存着等待处理,否则进入下个流程
3、是否达到最大线程数,若未达到,则创建新的线程处理当前传入的任务,否则交给线程池中的饱和策略进行处理。
流程图如下:
线程池的构造方法
实现类是:java.util.concurrent.ThreadPoolExecutor,主要构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
corePoolSize:核心线程大小,当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使有其他空闲线程可以处理任务也会创新线程,等到工作的线程数大于核心线程数时就不会在创建了。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前把核心线程都创造好,并启动
maximumPoolSize:线程池允许创建的最大线程数。如果队列满了,并且以创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。如果我们使用了无界队列,那么所有的任务会加入队列,这个参数就没有什么效果了
keepAliveTime:线程池的工作线程空闲后,保持存活的时间。如果没有任务处理了,有些线程会空闲,空闲的时间超过了这个值,会被回收掉。如果任务很多,并且每个任务的执行时间比较短,避免线程重复创建和回收,可以调大这个时间,提高线程的利用率
unit:keepAliveTIme的时间单位,可以选择的单位有天、小时、分钟、毫秒、微妙、千分之一毫秒和纳秒。类型是一个枚举java.util.concurrent.TimeUnit
workQueue:工作队列,用于缓存待处理任务的阻塞队列,常见的有4种,本文后面有介绍
threadFactory:线程池中创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字
handler:饱和策略,当线程池无法处理新来的任务了,那么需要提供一种策略处理提交的新任务,默认有4种策略
线程池的使用步骤:
1、调用构造方法创建线程池
2、调用线程池的方法处理任务execute()
3、关闭线程池shutdown()/shutdownNow()
demo例子
package com.example.demo.demo.threadPoolDemo;
import java.util.concurrent.*;
/**
* 线程池demo
* 实现类是:java.util.concurrent.ThreadPoolExecutor
* public ThreadPoolExecutor(int corePoolSize,
* int maximumPoolSize,
* long keepAliveTime,
* TimeUnit unit,
* BlockingQueue<Runnable> workQueue,
* ThreadFactory threadFactory,
* RejectedExecutionHandler handler) {
* }
*/
public class ThreadPoolDemo {
//线程池里占据最大数量为3
static ThreadPoolExecutor executor = new ThreadPoolExecutor(3,
5, 10L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
int j =i;
String task = "任务"+j;
executor.execute(()->{
// 模拟线程池运作
try {
TimeUnit.SECONDS.sleep(j);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+task+"任务结束");
});
}
executor.shutdown();
}
}
设置线程池数量为3,只要三个线程在运行
pool-1-thread-1任务0任务结束
pool-1-thread-2任务1任务结束
pool-1-thread-3任务2任务结束
pool-1-thread-1任务3任务结束
pool-1-thread-2任务4任务结束
pool-1-thread-3任务5任务结束
pool-1-thread-1任务6任务结束
pool-1-thread-2任务7任务结束
pool-1-thread-3任务8任务结束
pool-1-thread-1任务9任务结束
线程池中常见5种工作队列
任务太多的时候,工作队列用于暂时缓存待处理的任务,jdk中常见的5种阻塞队列:
ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按照先进先出原则对元素进行排序
LinkedBlockingQueue:是一个基于链表结构的阻塞队列,此队列按照先进先出排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool使用了这个队列。
SynchronousQueue :一个不存储元素的阻塞队列,每个插入操作必须等到另外一个线程调用移除操作,否则插入操作一直处理阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用这个队列
PriorityBlockingQueue:优先级队列,进入队列的元素按照优先级会进行排序
SynchronousQueue队列的线程池demo
package com.example.demo.demo.threadPoolDemo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 线程池中队列:SynchronousQueue
* 静态工厂方法Executors.newCachedThreadPool使用这个队列
* public static ExecutorService newCachedThreadPool() {
* return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
* 60L, TimeUnit.SECONDS,
* new SynchronousQueue<Runnable>());
* }
*/
public class ThreadPoolDemo2 {
static ExecutorService excutor = Executors.newCachedThreadPool();//队列SynchronousQueue
public static void main(String[] args) {
for (int i = 0; i <10 ; i++) {
int j =i;
String tackName = "任务"+j;
excutor.execute(()->{
try {
TimeUnit.SECONDS.sleep(j);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+tackName+"任务结束");
});
}
excutor.shutdown();
}
}
pool-1-thread-1任务0任务结束
pool-1-thread-2任务1任务结束
pool-1-thread-3任务2任务结束
pool-1-thread-4任务3任务结束
pool-1-thread-5任务4任务结束
pool-1-thread-6任务5任务结束
pool-1-thread-7任务6任务结束
pool-1-thread-8任务7任务结束
pool-1-thread-1任务8任务结束
pool-1-thread-9任务9任务结束
线程池内部方法
ThreadPoolExecutor内部提供了几个方法beforeExecute、afterExecute、terminated
package com.example.demo.demo.threadPoolDemo;
import java.util.concurrent.*;
/**
* ThreadPoolExecutor
* 线程池自带的方法
*/
public class ThreadPoolDemo3 {
public static class ThreadT1 implements Runnable{
String taskName;
public ThreadT1(String taskName) {
this.taskName=taskName;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在处理"+this.taskName);
}
@Override
public String toString() {
return "ThreadT1{" +
"taskName='" + taskName + '\'' +
'}';
}
}
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,10L,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), Executors.defaultThreadFactory(),(r, executors) -> {
System.out.println("无法执行的任务"+r.toString());
}){
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
System.out.println(System.currentTimeMillis() + "," + t.getName() + ",开始执行任务:" + r.toString());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + ",任务:" + r.toString() + ",执行完毕!");
}
@Override
protected void terminated() {
super.terminated();
System.out.println(System.currentTimeMillis() + "," + Thread.currentThread().getName() + ",关闭线程池!");
}
};
for (int i = 0; i <10 ; i++) {
executor.execute(new ThreadT1("任务"+i));
}
TimeUnit.SECONDS.sleep(1);
executor.shutdown();
}
}
D:\Java\jdk1.8.0_181\bin\java.exe "-javaagent:D:\JetBrains\IntelliJ IDEA 2019.3.2\lib\idea_rt.jar=50408:D:\JetBrains\IntelliJ IDEA 2019.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Java\jdk1.8.0_181\jre\lib\charsets.jar;D:\Java\jdk1.8.0_181\jre\lib\deploy.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;D:\Java\jdk1.8.0_181\jre\lib\javaws.jar;D:\Java\jdk1.8.0_181\jre\lib\jce.jar;D:\Java\jdk1.8.0_181\jre\lib\jfr.jar;D:\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;D:\Java\jdk1.8.0_181\jre\lib\jsse.jar;D:\Java\jdk1.8.0_181\jre\lib\management-agent.jar;D:\Java\jdk1.8.0_181\jre\lib\plugin.jar;D:\Java\jdk1.8.0_181\jre\lib\resources.jar;D:\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\63111\Desktop\springDemo\springBootDemo\target\classes;D:\repository\org\springframework\boot\spring-boot-starter-web\2.3.0.BUILD-SNAPSHOT\spring-boot-starter-web-2.3.0.BUILD-20200515.011136-707.jar;D:\repository\org\springframework\boot\spring-boot-starter\2.3.0.BUILD-SNAPSHOT\spring-boot-starter-2.3.0.BUILD-20200515.011136-707.jar;D:\repository\org\springframework\boot\spring-boot-starter-logging\2.3.0.BUILD-SNAPSHOT\spring-boot-starter-logging-2.3.0.BUILD-20200515.011136-707.jar;D:\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\repository\org\apache\logging\log4j\log4j-to-slf4j\2.13.2\log4j-to-slf4j-2.13.2.jar;D:\repository\org\apache\logging\log4j\log4j-api\2.13.2\log4j-api-2.13.2.jar;D:\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\repository\org\yaml\snakeyaml\1.26\snakeyaml-1.26.jar;D:\repository\org\springframework\boot\spring-boot-starter-json\2.3.0.BUILD-SNAPSHOT\spring-boot-starter-json-2.3.0.BUILD-20200515.011136-707.jar;D:\repository\com\fasterxml\jackson\core\jackson-databind\2.11.0\jackson-databind-2.11.0.jar;D:\repository\com\fasterxml\jackson\core\jackson-annotations\2.11.0\jackson-annotations-2.11.0.jar;D:\repository\com\fasterxml\jackson\core\jackson-core\2.11.0\jackson-core-2.11.0.jar;D:\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.11.0\jackson-datatype-jdk8-2.11.0.jar;D:\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.11.0\jackson-datatype-jsr310-2.11.0.jar;D:\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.11.0\jackson-module-parameter-names-2.11.0.jar;D:\repository\org\springframework\boot\spring-boot-starter-tomcat\2.3.0.BUILD-SNAPSHOT\spring-boot-starter-tomcat-2.3.0.BUILD-20200515.011136-707.jar;D:\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.35\tomcat-embed-core-9.0.35.jar;D:\repository\org\glassfish\jakarta.el\3.0.3\jakarta.el-3.0.3.jar;D:\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.35\tomcat-embed-websocket-9.0.35.jar;D:\repository\org\springframework\spring-web\5.2.6.RELEASE\spring-web-5.2.6.RELEASE.jar;D:\repository\org\springframework\spring-beans\5.2.6.RELEASE\spring-beans-5.2.6.RELEASE.jar;D:\repository\org\springframework\spring-webmvc\5.2.6.RELEASE\spring-webmvc-5.2.6.RELEASE.jar;D:\repository\org\springframework\spring-aop\5.2.6.RELEASE\spring-aop-5.2.6.RELEASE.jar;D:\repository\org\springframework\spring-context\5.2.6.RELEASE\spring-context-5.2.6.RELEASE.jar;D:\repository\org\springframework\spring-expression\5.2.6.RELEASE\spring-expression-5.2.6.RELEASE.jar;D:\repository\org\springframework\boot\spring-boot-devtools\2.3.0.BUILD-SNAPSHOT\spring-boot-devtools-2.3.0.BUILD-20200515.011136-707.jar;D:\repository\org\springframework\boot\spring-boot\2.3.0.BUILD-SNAPSHOT\spring-boot-2.3.0.BUILD-20200515.011136-707.jar;D:\repository\org\springframework\boot\spring-boot-autoconfigure\2.3.0.BUILD-SNAPSHOT\spring-boot-autoconfigure-2.3.0.BUILD-20200515.011136-707.jar;D:\repository\org\projectlombok\lombok\1.18.12\lombok-1.18.12.jar;D:\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\repository\org\springframework\spring-core\5.2.6.RELEASE\spring-core-5.2.6.RELEASE.jar;D:\repository\org\springframework\spring-jcl\5.2.6.RELEASE\spring-jcl-5.2.6.RELEASE.jar com.example.demo.demo.threadPoolDemo.ThreadPoolDemo3
1591608247391,pool-1-thread-1,开始执行任务:ThreadT1{taskName='任务0'}
pool-1-thread-1正在处理任务0
1591608247392,pool-1-thread-3,开始执行任务:ThreadT1{taskName='任务2'}
pool-1-thread-3正在处理任务2
1591608247392,pool-1-thread-2,开始执行任务:ThreadT1{taskName='任务1'}
pool-1-thread-2正在处理任务1
1591608247392,pool-1-thread-3,任务:ThreadT1{taskName='任务2'},执行完毕!
1591608247392,pool-1-thread-7,开始执行任务:ThreadT1{taskName='任务6'}
pool-1-thread-7正在处理任务6
1591608247392,pool-1-thread-7,任务:ThreadT1{taskName='任务6'},执行完毕!
1591608247392,pool-1-thread-1,任务:ThreadT1{taskName='任务0'},执行完毕!
1591608247392,pool-1-thread-4,开始执行任务:ThreadT1{taskName='任务3'}
1591608247392,pool-1-thread-6,开始执行任务:ThreadT1{taskName='任务5'}
pool-1-thread-6正在处理任务5
1591608247392,pool-1-thread-5,开始执行任务:ThreadT1{taskName='任务4'}
pool-1-thread-5正在处理任务4
1591608247393,pool-1-thread-5,任务:ThreadT1{taskName='任务4'},执行完毕!
1591608247393,pool-1-thread-10,开始执行任务:ThreadT1{taskName='任务9'}
pool-1-thread-10正在处理任务9
1591608247393,pool-1-thread-10,任务:ThreadT1{taskName='任务9'},执行完毕!
1591608247392,pool-1-thread-2,任务:ThreadT1{taskName='任务1'},执行完毕!
1591608247393,pool-1-thread-9,开始执行任务:ThreadT1{taskName='任务8'}
pool-1-thread-9正在处理任务8
1591608247393,pool-1-thread-9,任务:ThreadT1{taskName='任务8'},执行完毕!
1591608247393,pool-1-thread-8,开始执行任务:ThreadT1{taskName='任务7'}
1591608247393,pool-1-thread-6,任务:ThreadT1{taskName='任务5'},执行完毕!
pool-1-thread-4正在处理任务3
1591608247393,pool-1-thread-4,任务:ThreadT1{taskName='任务3'},执行完毕!
pool-1-thread-8正在处理任务7
1591608247394,pool-1-thread-8,任务:ThreadT1{taskName='任务7'},执行完毕!
1591608248393,pool-1-thread-8,关闭线程池!
Process finished with exit code 0
从输出结果中可以看到,每个需要执行的任务打印了3行日志,执行前由线程池的beforeExecute打印,执行时会调用任务的run方法,任务执行完毕之后,会调用线程池的afterExecute方法,从每个任务的首尾2条日志中可以看到每个任务耗时2秒左右。线程池最终关闭之后调用了terminated方法