线程是一个重量级的对象,应该尽量避免频繁创建和销毁
1.线程池的设计思路
一般的池化资源的设计,都是涉及到两个办法:
class XXXPool{
// 获取池化资源
XXX acquire() {
}
// 释放池化资源
void release(XXX x){
}
}
但是这种设计思路不适用于线程池,因为假如从池中拿到了一个Thread对象,但是Thread对象根本没有 execute(Runnable target) 这样的公共方法
因此,线程池采取的是一种生产者-消费者的模式:线程的使用方式生产者,线程池本身是消费者:
package main.com.lingwuee.zhang.JKChapter22;
import jdk.nashorn.internal.ir.Block;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
/**
* 简化的线程池模型
* @Auther: zhang_yx
* @Date: 2021/8/17 10:53
*/
public class MyThreadPool{
private BlockingQueue<Runnable> workQueue;
private List<WorkThread> threads = new ArrayList<>();
public MyThreadPool(int poolSize, BlockingQueue workQueue) {
this.workQueue = workQueue;
for (int i = 0; i < poolSize; i++) {
WorkThread workThread = new WorkThread();
workThread.run();
threads.add(workThread);
}
}
public void executor(Runnable command){
try {
workQueue.put(command);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class WorkThread extends Thread{
@Override
public void run() {
while (true){
try {
Runnable task = workQueue.take();
task.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2.如何使用Java中的线程池
ThreadPoolExecutor 的构造函数非常复杂,如下面代码所示,这个最完备的构造函数有 7 个参数。
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
可以将线程池比作一个项目组,而线程比较组内的组员,使用者只需要将项目丢到项目组中交由员工执行
- corePoolSize:核心线程数,表示线程池保有的最小线程数。那么没有任务,也会维持这些线程,JDK1.6新增的allowCoreThreadTimeOut(boolean value) ,可以让核心线程数超过keepAliveTime & unit的时间也被回收
- maximumPoolSize:表示线程池创建的最大线程数,当工作队列已经满了的时候,会新增线程到maximumPoolSize,如果此时队列满了且线程数达到了maximumPoolSize,此时就会采取拒绝策略
- keepAliveTime & unit:当线程数量大于核心线程数时候,并线程空闲时间大于keepAliveTime & unit则会回收多余线程
- workQueue:工作队列,存储待执行的任务
- threadFactory:自定义如何创建线程,例如给线程指定一个名称等
- handler:自定义拒绝策略,当队列满+线程数达到了最大线程数,此时新来的任务就会拒绝,ThreadpoolExecutor已经提供了下面四种策略:
- CallerRunsPolicy:提交任务的线程自己去执行任务
- AbortPolicy:默认拒绝策略,会 throws RejectedExecutionException
- DiscardPolicy:直接丢弃任务,没有任何异常抛出
- DiscardOldestPolicy:丢弃最老的任务,其实就是将最早进入队列的任务丢弃,然后将新任务加入队列
3.使用线程池的注意点
- Java 并发包里提供了一个线程池的静态工厂类 Executors,利用 Executors 你可以快速创建线程池,但是不建议使用。原因是Executors 提供的很多方法默认使用的是无界的LinkedBlockingQueue,高负载情况下,无界队列很容易产生OOM,因此强烈建议使用有界队列
- 使用有界队列,可能会触发默认拒绝策略,从而throws RejectExecutionException,该异常是允许时异常,因此需要注意catch并处理