线程池概念
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
使用线程池目的
避免频繁的创建销毁线程,复用创建的线程。
线程池处理流程
通过上图,我们看到了线程池的主要处理流程。我们的关注点在于,任务提交之后是怎么执行的。大致如下:
- 判断核心线程池是否已满,如果不是,则创建线程执行任务
- 如果核心线程池满了,判断队列是否满了,如果队列没满,将任务放在队列中
- 如果队列满了,则判断线程池是否已满,如果没满,创建线程执行任务
- 如果线程池也满了,则按照拒绝策略对任务进行处理
线程池实际使用
线程池的创建
- 使用Executors创建:Executors是线程池工厂类,不需要我们自己去实现,实际创建的是ThreadPoolExecutor实例。
- 自己手动创建ThreadPoolExecutor实例
注意:阿里Java开发手册要求【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
线程池相关接口/类继承关系如下
对比Executor和new Thread()
new Thread的弊端如下:
- 每次new Thread新建对象性能差。
- 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
- 缺乏更多功能,如定时执行、定期执行、线程中断。
Java提供的四种线程池的好处在于:
- 重用存在的线程,减少对象创建、消亡的开销,性能佳。
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
- 提供定时执行、定期执行、单线程、并发数控制等功能。
Executor 和 ExecutorService 接口主要的区别:
- ExecutorService 接口继承了 Executor 接口,是 Executor 的子接口
- Executor 接口定义了 execute()方法用来接收一个Runnable接口的对象,而 ExecutorService 接口中的 submit()方法重载可以接受两种Runnable和Callable接口的对象。
- Executor 中的 execute() 方法不返回任何结果,而 ExecutorService 中的 submit()方法可以通过一个 Future 对象返回运算结果。
- ExecutorService 还提供用来控制线程池的方法。比如:调用 shutDown() 方法终止线程池。
Executors使用
省略,参考见下文
ThreadPoolExecutor使用
1、构建ThreadPoolExecutor对象,构造方法参数:
序号 | 名称 | 类型 | 含义 |
---|---|---|---|
1 | corePoolSize | int | 核心线程池大小 |
2 | maximumPoolSize | int | 最大线程池大小 |
3 | keepAliveTime | long | 线程最大空闲时间 |
4 | unit | TimeUnit | 时间单位 |
5 | workQueue | BlockingQueue | 线程等待队列 |
6 | threadFactory | ThreadFactory | 线程创建工厂 |
7 | handler | RejectedExecutionHandler | 拒绝策略 |
2、ThreadPoolExecutor的处理流程
3、自定义ThreadPoolExecutor线程池
import java.io.IOException;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadTest {
public static void main(String[] args) throws InterruptedException, IOException {
int corePoolSize = 2;
int maximumPoolSize = 4;
long keepAliveTime = 10;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
ThreadFactory threadFactory = new NameTreadFactory();
RejectedExecutionHandler handler = new MyIgnorePolicy();
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
workQueue, threadFactory, handler);
executor.prestartAllCoreThreads(); // 预启动所有核心线程
for (int i = 1; i <= 10; i++) {
MyTask task = new MyTask(String.valueOf(i));
executor.execute(task);
}
Thread.sleep(Integer.MAX_VALUE); //阻塞主线程
}
static class NameTreadFactory implements ThreadFactory {
private final AtomicInteger mThreadNum = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
System.out.println(t.getName() + " has been created");
return t;
}
}
public static class MyIgnorePolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
doLog(r, e);
}
private void doLog(Runnable r, ThreadPoolExecutor e) {
// 可做日志记录等
System.err.println( r.toString() + " rejected");
}
}
static class MyTask implements Runnable {
private String name;
public MyTask(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(this.toString() + " is running!");
Thread.sleep(3000); //让任务执行慢点
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getName() {
return name;
}
@Override
public String toString() {
return "MyTask [name=" + name + "]";
}
}
}
Console log
my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
MyTask [name=2] is running!
MyTask [name=1] is running!
my-thread-4 has been created
MyTask [name=3] is running!
MyTask [name=6] is running!
MyTask [name=7] rejected
MyTask [name=8] rejected
MyTask [name=9] rejected
MyTask [name=10] rejected
MyTask [name=4] is running!
MyTask [name=5] is running!
ExecutorCompletionService使用
ExecutorCompletionService提供了对线程池的封装
注意:处理流程判断顺序:核心线程->队列->非核心线程->拒绝策略
对比completionService take poll方法
- completionService.take()// 阻塞,直到有任务完成可以获取结果
- completionService.poll()//poll直接返回,不阻塞。但是没有完成的任务则返回null
- completionService.poll(5,TimeUnit.SECONDS) //阻塞等待指定时间,如果有完成结果返回,没有的直接返回null
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ExecutorCompletionServiceUtil {
/**
* 线程数(处理器数量)
*/
private static final int threadCount = Runtime.getRuntime().availableProcessors();
public static void main(String[] args) {
ExecutorCompletionServiceUtil executorCompletionServiceUtil = new ExecutorCompletionServiceUtil();
executorCompletionServiceUtil.test();
}
public void test() {
System.out.println("threadCount:" + threadCount);
Executor executor = Executors.newFixedThreadPool(threadCount);
ExecutorCompletionService service = new ExecutorCompletionService(executor);
for (int i = 0; i < 100; i++) {
service.submit(new TaskDemo(i));
}
List<String> list = new ArrayList<>(100);
for (int i = 0; i < 100; i++) {
try {
String result = (String) service.take().get();
System.out.println(result);
list.add(result);
} catch (InterruptedException e) {
System.err.println("InterruptedException" + e);
} catch (ExecutionException e) {
System.err.println("ExecutionException" + e);
}
}
System.out.println("result:" + list);
System.out.println("result size:" + list.size());
}
/**
* 执行任务需要实现Callable接口
*/
class TaskDemo implements Callable<String> {
private int number;
public TaskDemo(int number) {
this.number = number;
}
@Override
public String call() throws Exception {
return "Task:" + Thread.currentThread().getName() + " number:" + number;
}
}
}
Console log
threadCount:8
Task:pool-1-thread-1 number:0
Task:pool-1-thread-4 number:3
Task:pool-1-thread-2 number:1
.........................
Task:pool-1-thread-3 number:96
Task:pool-1-thread-7 number:97
result:[Task:pool-1-thread-1 number:0, Task:pool-1-thread-4 number:3, ...................Task:pool-1-thread-3 number:96, Task:pool-1-thread-7 number:97]
result size:100
参考:
- 线程池实现原理参考 https://www.jianshu.com/p/7ab4ae9443b9
- 线程池配置参数参考https://blog.csdn.net/q669239799/article/details/90680701
- ThreadPoolExecutor参数、方法详解 https://www.jianshu.com/p/c41e942bcd64
- ThreadPoolExecutor使用,自定义 https://www.jianshu.com/p/f030aa5d7a28
- ExecutorCompletionService原理分析 https://www.jianshu.com/p/9b85c47a6ff4
- CompletionService和ExecutorCompletionService详解 https://www.jianshu.com/p/cfda708a3478