目录
1、参数说明
下面是JDK线程池最全参数的构造方法,所有的Executors构造的线程池都是基于该方法,只不过给你封装了很多参数,简化了,你拿着就用。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize: 核心线程数
maximumPoolSize: 最大线程数
keepAliveTime: 生存时间--针对救急线程
unit: 时间单位--针对救急线程
workQueue: 阻塞队列
-SynchronousQueue :同步阻塞队列
-LinkedBlockingQueue :基于链表的无界阻塞队列
-ArrayBlockingQueue :基于数组的有界阻塞队列
threadFactory: 线程工厂--便于管理,如:起个方便日志跟踪定位的名称
handle: 据决策略
-AbortPolicy:默认策略,让调用者抛出RejectedExecutionException异常
-CallerRunsPolicy:让调用者自己运行任务
-DiscardPolicy:放弃本次任务
-DiscardOldestPolicy:放弃队列中最早的任务,本任务取而代之
2、线程池运行流程
3、线程池状态
ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量
状态名 | 高3位 | 接受新任务 | 处理阻塞队列任务 | 说明 |
RUNNING | 111 | Y | Y | 011>010>001>000>111(第一位表示负数) |
SHUTDOWN | 000 | N | Y | 不会接受新任务,但会处理阻塞队列剩余任务 |
STOP | 001 | N | N | 会中断正在执行的任务,并抛弃阻塞队列任务 |
TIDYING | 010 | — | — | 任务全执行完毕,活动线程为0即将进入终结 |
TERMINATED | 011 | — | — | 终结状态 |
4、newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点:
核心线程数 == 最大线程数(没有救急线程被创建),因此无需超时时间 0L
阻塞队列是无界的,可以放任意数量的任务
使用场景:适用于任务量已知,相对耗时的任务(长连接)
5、newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
特点:
核心线程数是0,最大线程数是Integer.MAX_VALUE,救急线程的空闲时间是60s,意味着
-全部都是救急线程(60s后可回收)
-救急线程可以"无限"创建(Integer.MAX_VALUE)
队列采用SynchronousQueue实现特点是,它没有容量,没有线程来取是放不进去的(类似生活中:一手交钱,一手交货)
SynchronousQueue:举个栗子,下面有本项目搭建展示
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.SynchronousQueue;
/**
* @Author George
* @Date 2022/11/11 6:44
* @VDesc
*/
@Slf4j
public class TestExecutors {
public static void main(String[] args) {
SynchronousQueue<Integer> syncQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
log.debug("putting {}",1);
syncQueue.put(1);
log.debug("put end {}",1);
log.debug("putting {}",2);
syncQueue.put(2);
log.debug("put end {}",2);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1").start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
try {
log.debug("taking {}",1);
syncQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t2").start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
try {
log.debug("taking {}",2);
syncQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t3").start();
}
输出:
08:51:19.613 [t1] com.example.threadpool.TestExecutors - putting 1
08:51:20.616 [t2] com.example.threadpool.TestExecutors - taking 1
08:51:20.617 [t1] com.example.threadpool.TestExecutors - put end 1
08:51:20.618 [t1] com.example.threadpool.TestExecutors - putting 2
08:51:21.616 [t3] com.example.threadpool.TestExecutors - taking 2
08:51:21.616 [t1] com.example.threadpool.TestExecutors - put end 2说明:
可以看到t1往任务队列放任务的时放不进去,1s后,当t2来取的任务时候它才 put end;接着t1放任务2,还是要在等1s后,线程t3来取任务时,才能 put end 2
使用场景:整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲1分钟后释放线程。
适合任务数比较密集,但每个任务执行时间较短的情况(短连接)
项目搭建:
创建项目只需要导入2个依赖和一个log日志打印模板
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!-- log -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{HH:mm:ss.SSS} [%t] %logger - %m%n</pattern>
</encoder>
</appender>
<logger name="com.example" level="debug" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<root level="ERROR">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
6、 newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
使用场景:
希望多个任务排队执行(串行)。线程数固定为1,任务数多于1时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放。
比较区别:
-自己创建一个单线程串行执行任务,如果任务执行失败而终止,那么没有任何补救措施;而线程池还会新建一个线程,保证池的正常工作。
private static void testSingleExecutor() {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(() -> {
log.debug("任务1");
int i = 1 / 0; // ArithmeticException
});
singleThreadExecutor.execute(() -> {
log.debug("任务2");
});
singleThreadExecutor.execute(() -> {
log.debug("任务3");
});
}
可以导入上面的TestExecutors类中执行
09:09:38.815 [pool-1-thread-1] com.example.threadpool.TestExecutors - 任务1
09:09:38.825 [pool-1-thread-2] com.example.threadpool.TestExecutors - 任务2
09:09:38.826 [pool-1-thread-2] com.example.threadpool.TestExecutors - 任务3
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at com.example.threadpool.TestExecutors.lambda$main$0(TestExecutors.java:22)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)说明:
可以看到线程池创建的第一个线程由于异常结束了,但是任务2、3没有因为[pool-1-thread-1] 线程1中断而终止下面任务的执行。
-Executors.newSingleThreadExecutor()线程数始终为1,不能修改
-FinalizableDelegatedExecutorService应用的是装饰器模式,只对外暴露了ExecutorService接口,因此不能调用ThreadPoolExecutor中特有的方法
-Executors.newFixedThreadPool(1)初始时为1,以后可以修改
-对外暴露的是ThreaPoolExecutor对象,可以强转后调用setCorePoolSize等方法经行修改
7、提交任务
// 执行任务
void executor()
// 提交任务task,返回值Future 获得任务执行结果
<T> Future<T> submit(Callable<T> task);
// 提交 task 中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throw InterruptedException;
// 提交 tasks 中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit) throw InterruptedException;
// 提交 tasks中所有任务,那个任务先执行成功执行完毕,返回任务执行结果,其他任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throw InterruptedException, ExecutionException;
// 提交 tasks中所有任务,那个任务先执行成功执行完毕,返回任务执行结果,其他任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throw InterruptedException, ExecutionException, TimeoutException;
testSubmit()
private static void testSubmit() throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<String> future = executorService.submit(() -> {
try {
log.debug("executor running....");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok";
});
log.debug("主线程接收线程池返回对象{}",future.get());
}
testInvokeAll()
private static void testInvokeAll() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Future<String>> futures = executorService.invokeAll(Arrays.asList(
() -> {
log.debug("begin 1");
Thread.sleep(1000);
return "1";
},
() -> {
log.debug("begin 2");
Thread.sleep(500);
return "2";
},
() -> {
log.debug("begin 3");
Thread.sleep(2000);
return "3";
}
));
testInvokeAny()
private static void testInvokeAny() throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 核心线程从 2 -> 1,收到的最终结果1,因为其他任务会放入队列,等1已经有结果了,invokeAny就返回了
String res = executorService.invokeAny(Arrays.asList(
() -> {
log.debug("begin 1");
Thread.sleep(1000);
log.debug("end 1");
return "1";
},
() -> {
log.debug("begin 2");
Thread.sleep(500);
log.debug("end 2");
return "2";
},
() -> {
log.debug("begin 3");
Thread.sleep(2000);
log.debug("end 3");
return "3";
}
));
log.debug("收到的最终结果{}",res);
}
7、关闭线程池
shutdown
线程池状态变为 SHUTDOWN;不会接受新任务;但已提交任务会执行完;此方法不会阻塞调用线程的执行。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改线程池状态 RUNNING -> SHUTDOWN
advanceRunState(SHUTDOWN);
// 仅会打断空闲线程
interruptIdleWorkers();
onShutdown(); // 扩展点,给子类用的 ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 尝试终结(没有可运行的线程,可以立刻终结,如果还有运行的线程也不会等,没有运行完的线程让它自己运行完自己去结束)
tryTerminate();
}
shutdownNow
线程池状态变为 STOP ;不会接受新任务,会将队列中的任务返回;并用interrupt的方式终断正在执行的任务
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 修改线程状态
advanceRunState(STOP);
// 打断所有线程
interruptWorkers();
// 获取队列中剩余任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 尝试终结
tryTerminate();
return tasks;
}
其他方法
// 不在RUNNING 状态的线程池,此方法就返回 true
boolean isShutdown();
// 线程池状态是否是 TERMINATED
boolean isTerminated();
// 调用 shutdown 后,由于调用线程并不会等待所有任务运行结束,因此如果想在线程池 TERMINATED 后做些事情,可以利用此方法等待
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException
举个栗子:
shutdown()方法测试
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Author George
* @Date 2022/11/12 22:28
* @VDesc
*/
@Slf4j
public class TestShutDown {
public static void main(String[] args) {
testShutDown();
}
private static void testShutDown() {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(() -> {
log.debug("task 1 running.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("task 1 finish.....");
return 1;
});
executorService.submit(() -> {
log.debug("task 2 running.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("task 2 finish.....");
return 2;
});
executorService.submit(() -> {
log.debug("task 3 running.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("task 3 finish.....");
return 3;
});
// 第1步,已提交(线程运行的,阻塞队列中的)任务会执行完,不会阻塞调用线程的执行
log.debug("shutdown");
executorService.shutdown();
log.debug("run other....");
// 第2步,调用shutdown方法之后,再去提交任务会抛异常
executorService.submit(() -> {
log.debug("task 4 running.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("task 4 finish.....");
return 4;
});
}
}
22:37:05.058 [main] com.example.threadpool.TestShutDown - shutdown
22:37:05.063 [main] com.example.threadpool.TestShutDown - run other....
22:37:05.058 [pool-1-thread-1] com.example.threadpool.TestShutDown - task 1 running.....
22:37:05.058 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 2 running.....
22:37:06.121 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 2 finish.....
22:37:06.121 [pool-1-thread-1] com.example.threadpool.TestShutDown - task 1 finish.....
22:37:06.121 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 3 running.....
22:37:07.135 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 3 finish.....说明:
已提交(正在运行的线程【1、2线程】、阻塞队列中的【3线程】)任务会执行完毕,不会阻塞调用线程的执行(也就是main线程),main执行完shutdown方法之后,马上执行了run other(可以看到没有阻塞)
shutdownNow()方法测试
private static void testShutDownNow() {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(() -> {
log.debug("task 1 running.....");
Thread.sleep(1000);
log.debug("task 1 finish.....");
return 1;
});
executorService.submit(() -> {
log.debug("task 2 running.....");
Thread.sleep(1000);
log.debug("task 2 finish.....");
return 2;
});
executorService.submit(() -> {
log.debug("task 3 running.....");
Thread.sleep(1000);
log.debug("task 3 finish.....");
return 3;
});
// 已提交(线程运行的,阻塞队列中的)任务会被打断,终止执行
log.debug("shutdownNow");
List<Runnable> runnables = executorService.shutdownNow();
log.debug("run other.... {}",runnables);
}
22:46:18.385 [main] com.example.threadpool.TestShutDown - shutdown
22:46:18.388 [pool-1-thread-2] com.example.threadpool.TestShutDown - task 2 running.....
22:46:18.386 [pool-1-thread-1] com.example.threadpool.TestShutDown - task 1 running.....
22:46:18.391 [main] com.example.threadpool.TestShutDown - run other.... [java.util.concurrent.FutureTask@2a18f23c]说明:
可以看到1、2线程随着shutdownNow的执行,2个都终止运行了;线程3提交到阻塞队列的任务是可以返回接收的
白首为功名,旧山松竹老,阻归程。欲将心事付瑶琴,知音少,弦断有谁听?