前言
我们可以通过JDK API查询了解
ThreadPoolExecutor
构造方法摘要(还有三个没c)
public ThreadPoolExecutor(int corePoolSize,//核心线程数(8020原则)
int maximumPoolSize,//最大线程数量(一般:最大任务-队列长度*单个时间)
long keepAliveTime,//最大空闲时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列长度(一般:核心/单个时间*2)
ThreadFactory threadFactory,//线程工厂(可选)
RejectedExecutionHandler handler)//饱和处理机制(可选)
//用给定的初始参数创建新的 ThreadPoolExecutor。
我们为什么要使用线程池呢
使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力;当然了,使用线程池的原因不仅仅只有这些,我们可以从线程池自身的优点上来进一步了解线程池的好处;
线程池有哪些优势
1:线程和任务分离,提升线程重用性;
2:控制线程并发数量,降低服务器压力,统一管理所有线程;
3:提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
JDK API中有如下方法(详情自己查)
类型 | 方法 | 说明 |
---|---|---|
protected void | afterExecute(Runnable r, Throwable t) | 基于完成执行给定 Runnable 所调用的方法。 |
void | allowCoreThreadTimeOut(boolean value) | 如果在保持活动时间内没有任务到达,新任务到达时正在替换(如果需要),则设置控制核心线程是超时还是终止的策略。 |
boolean | allowsCoreThreadTimeOut() | 如果此池允许核心线程超时和终止,如果在 keepAlive 时间内没有任务到达,新任务到达时正在替换(如果需要),则返回 true。 |
boolean | awaitTermination(long timeout, TimeUnit unit) | 请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行。 |
protected void | beforeExecute(Thread t, Runnable r) | 在执行给定线程中的给定 Runnable 之前调用的方法。 |
void | execute(Runnable command) | 在将来某个时间执行给定任务。 |
protected void | finalize() | 当不再引用此执行程序时,调用 shutdown。 |
int | getActiveCount() | 返回主动执行任务的近似线程数。 |
long | getCompletedTaskCount() | 返回已完成执行的近似任务总数。 |
int | getCorePoolSize() | 返回核心线程数。 |
long | getKeepAliveTime(TimeUnit unit) | 返回线程保持活动的时间,该时间就是超过核心池大小的线程可以在终止前保持空闲的时间值。 |
int | getLargestPoolSize() | 返回曾经同时位于池中的最大线程数。 |
int | getMaximumPoolSize() | 返回允许的最大线程数。 |
int | getPoolSize() | 返回池中的当前线程数。 |
BlockingQueue | getQueue() | 返回此执行程序使用的任务队列。 |
RejectedExecutionHandler | getRejectedExecutionHandler() | 返回用于未执行任务的当前处理程序。 |
long | getTaskCount() | 返回曾计划执行的近似任务总数。 |
ThreadFactory | getThreadFactory() | 返回用于创建新线程的线程工厂。 |
boolean | isShutdown() | 如果此执行程序已关闭,则返回 true。 |
boolean | isTerminated() | 如果关闭后所有任务都已完成,则返回 true。 |
boolean | isTerminating() | 如果此执行程序处于在 shutdown 或 shutdownNow 之后正在终止但尚未完全终止的过程中,则返回 true。 |
int | prestartAllCoreThreads() | 启动所有核心线程,使其处于等待工作的空闲状态。 |
boolean | prestartCoreThread() | 启动核心线程,使其处于等待工作的空闲状态。 |
void | purge() | 尝试从工作队列移除所有已取消的 Future 任务。 |
boolean | remove(Runnable task) | 从执行程序的内部队列中移除此任务(如果存在),从而如果尚未开始,则其不再运行。 |
void | setCorePoolSize(int corePoolSize) | 设置核心线程数。 |
void | setKeepAliveTime(long time, TimeUnit unit) | 设置线程在终止前可以保持空闲的时间限制。 |
void | setMaximumPoolSize(int maximumPoolSize) | 设置允许的最大线程数。 |
void | setRejectedExecutionHandler(RejectedExecutionHandler handler) | 设置用于未执行任务的新处理程序。 |
void | setThreadFactory(ThreadFactory threadFactory) | 设置用于创建新线程的线程工厂。 |
void | shutdown() | 按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务。 |
List | shutdownNow() | 尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表。 |
protected | void terminated() | 当 Executor 已经终止时调用的方法。 |
Runnable和Callable的区别是:
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。
它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
然后写一个线程池
1.编写一个任务类,实现Runnable接口
public class MyTask implements Runnable{
private int id;
//由于run方法是重写接口的方法,因此id这个属性初始化可以利用构造方法完成
public MyTask(int id){//构造方法
this.id=id;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("线程:"+name+"即将执行的任务:"+id);
try {
Thread.sleep(200);//睡眠0.2s
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("线程:"+name+"完成了任务:"+id);
}
//重写toString方法,让线程名字好看(易懂)
@Override
public String toString() {
return "MyTask{" + "id=" + id + '}';
}
}
2.编写一个线程类,需要继承Thread类
设计一个属性,用于保存线程名字;
设计一个集合,用于保存所有任务;
public class Myworker extends Thread{
private String name; //保存线程的名字
private List<Runnable> tasks;//保存所有任务;
//利用构造方法,给成员变量赋值
public Myworker(String name, List<Runnable> tasks) {
super(name);
this.tasks = tasks;
}
@Override
public void run() {
//判断集合中是否有任务,只要有就一直执行任务
while (tasks.size()>0){
//remove把线程从集合中移除出来
Runnable r =tasks.remove(0);
//然后执行
r.run();
}
}
}
3.自定义线程池
- 成员变量:
1.任务队列 集合
2.当前线程数量
3.核心线程数
4.最大线程数
5.任务队列的长度 - 成员方法:
1.提交任务: 将任务添加到集合中,判断是否超出了任务总长度
2.执行任务: 判断当前线程的数量,决定创建核心线程还是非核心线程
public class MyThreadPool {
/**
* 任务队列 集合 需要控制线程安全问题
*/
private List<Runnable> tasks= Collections.synchronizedList(new LinkedList<>());
/**
* 当前线程数量
*/
private int num;
/**
* 核心线程数量
*/
private int corePoolSize;
/**
* 最大线程数量
*/
private int maxSize;
/**
* 任务队列的长度
*/
private int workSize;
public MyThreadPool(int corePoolSize, int maxSize, int workSize) {//构造
this.corePoolSize = corePoolSize;
this.maxSize = maxSize;
this.workSize = workSize;
}
//提交任务:
public void submit(Runnable r){
//判断当前集合中任务的数量,是否超出了最大任务数量(线程池的长度)
if(tasks.size()>=workSize){//超出了就丢弃
System.out.println("任务:"+r+"被丢弃了...");
}else {//没超出就添加
tasks.add(r);
//执行任务(自己写的方法)
execTask(r);
}
}
//执行任务(不给别用的方法最好都私有化)
private void execTask(Runnable r) {
//判断当前线程池中的线程数量,是否超过核心数
if(num<corePoolSize){//不超过就创建核心线程
new Myworker("核心线程:"+num, tasks).start();
num++;//线程数加一
}else if(num<maxSize){//超过就创建非核心线程
new Myworker("非核心线程:"+num, tasks).start();
num++;//线程数加一
}else {//超过了最大线程数就缓存起来
System.out.println("任务:"+r+"被缓存了...");
}
}
}
4.创建测试类,测试
1.创建线程池对象
2.提交多个任务
public class MyTest {
public static void main(String[] args) {
//创建线程池类(核心线程,最大线程,线程池长度=核心2/每个线程执行时间0.2s*2=20)
MyThreadPool pool=new MyThreadPool(2,4, 20);
//提交多个任务(提交了21个,超过了20个然后有一个被丢弃了)
for (int i=0;i<21;i++){
//创建任务对象,并提交给线程池
MyTask my=new MyTask(i);
pool.submit(my);
}
}
}