文章目录
- 线程池的状态
- ThreadPoolExecutor - 构造方法
- Executors - 固定大小的线程池
- Executors - 定时线程池
- Executors - 带缓冲线程池
- Executors - 单线程线程池
- 线程池常用方法
线程池的状态
线程池的5种状态
RUNNING: 线程池处在 RUNNING 状态时,能够接收新任务,以及对已添加的任务进行处理。该状态是线程池的初始状态,线程池一旦被创建,就处于 RUNNING 状态
SHUTDOWN: 线程池处于 SHUTDOWN 状态时,不接收新任务,但能处理等待队列中的任务。线程池在 RUNNING 状态下,调用 shutdown() 方法,会变成 SHUTDOWN 状态。
STOP: 线程池处于 STOP 状态时,不接收新任务,不再处理等待队列中的任务,并且会中断正在处理的任务,线程池在 RUNNING 状态下,调用 shutdownNow() 方法,变为 STOP 状态
TIDYING: 所有的任务都销毁了,工作线程数量为0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()
线程池在 SHUTDOWN 状态时,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN 状态变为 TIDYING 状态;
线程池在 STOP 状态时,线程池中执行的任务为空时,就会由 STOP 状态变为 TIDYING 。
TERMINATED: terminated() 方法执行之后,线程池彻底终止,就变成 TERMINATED 状态。
ThreadPoolExecutor - 构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize 核心线程数(最多保留的线程数)
- maximumPoolSize 最大线程数目
- keepAliveTime 线程执行完任务后的空闲时间 - 针对救急线程
- unit 时间单位 - 针对救急线程
- workQueue 阻塞队列
- threadFactory 线程工厂 - 可以为线程创建时起个好名字
- handler 拒绝策略
在线程池中分为核心线程和救急线程,救急线程一般是在任务阻塞队列(有界队列)满了之后才会被创建的。
corePoolSIze + 救急线程数 <= maximumPoolSize 。
救急线程存在生存时间(由keepAliveTime 决定),核心线程则没有。
当救急线程也满了(都在执行任务),如果再来一个任务,就会执行拒绝策略
Executors类和ThreadPoolExecutor都是util.concurrent并发包下面的类, Executos下面的newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor、newCachedThreadPool底层的实现都是用的ThreadPoolExecutor(构造时设置不同的参数)实现的,所以ThreadPoolExecutor更加灵活。
Executors - 固定大小的线程池
特点:
- 核心线程数 = 最大线程数 (没有救急线程被创建),因此无需超时时间
- 阻塞队列是无界的,可以放任意数量的任务
测试代码:
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
for (int i=0; i<5; i++){
int j = i;
fixedThreadPool.execute(()->{
System.out.println("当前线程是"+Thread.currentThread().getName()+" --------"+j);
});
}
}
执行结果:
适用于任务量已知,相对耗时的任务
Executors - 定时线程池
测试代码:
import java.util.concurrent.*;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
/**
* 延时执行任务(执行一次)
*/
pool.schedule(()->{
System.out.println("延时执行任务task1");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},1,TimeUnit.SECONDS);
pool.schedule(()->{
System.out.println("延时执行任务task2");
},1,TimeUnit.SECONDS);
/**
* 以一定的速率执行任务(执行一次)
*/
pool.scheduleAtFixedRate(()->{
System.out.println("以固定的速率执行------scheduleAtFixedRate");
},1,3,TimeUnit.SECONDS);
/**
* 以一定的延时执行任务(执行一次)。跟固定速率不同,这个延时以上个任务结束为坐标
*/
pool.scheduleWithFixedDelay(()->{
System.out.println("以固定的延时执行------scheduleAtFixedRate");
},1,3,TimeUnit.SECONDS);
}
}
执行结果:
Executors - 带缓冲线程池
特点:
- 核心线程数是0,最大线程数是Integer.MAX_VALUE,救急线程的空闲生存时间是60s
- 全部都是救急线程(60s后可以回收)
- 救急线程可以无限创建
- 队列采用SynchronousQueue实现特点是,它没有容量(可以理解是容量为0)
整个线程池表现为线程数会根据任务量不断增加,没有上限,当任务执行完毕,空闲一分钟后释放线程。适合做任务比较密集,但每个任务执行时间较短的情况。
Executors - 单线程线程池
使用场景:
希望多个任务排列执行。线程数固定为1,任务数大于1,会放入无界队列排队。任务执行完毕,这个唯一的线程也不会被释放。
区别:
- newSingleThreadExecutor与自己创建一个线程
- 自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池会新建一个线程,保证线程池的正常运行
- newSingleThreadExecutor与newFixedThreadPool(1)
- 【newSingleThreadExecutor】只对外暴露ExecutorService接口,一次不能调用ThreadPoolExecutor中特有的方法
- 【newFixedThreadPool】对外暴露的是ThreadPoolExecutor对象,可以强转后调用setCorePoolSize等方法进行修改
线程池常用方法
提交任务
1、 void execute(Runnable runnable);
单纯的执行任务,没有返回结果。
ExecutorService executorService = Executors.newFixedThreadPool(2);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Runnable输出信息");
}
};
executorService.execute(runnable);
// lamda写法
executorService.execute(()->{ System.out.println("Runnable输出信息");});
2、 Future submit(Callable task);
提交任务task,用返回值Future获取任务执行结果
ExecutorService executorService = Executors.newFixedThreadPool(2);
Callable callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "带返回值的执行任务1";
}
};
Future future1 = executorService.submit(callable);
System.out.println(future1.get());
// lamda写法
Future<String> future2 = executorService.submit(() -> {
return "带返回值的执行任务2";
});
System.out.println(future2.get());
3、 List<Future> invokeAll(Collection<? extends Callable> tasks)
throws InterruptedException;
提交tasks中所有的任务,接收一个集合对象(里面是Callsble对象),返回值是一个集合对象(里面是Future对象)
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Future<Object>> futures = executorService.invokeAll(Arrays.asList(
() -> {
return "invokeAll任务结果1";
},
() -> {
return "invokeAll任务结果2";
},
() -> {
return "invokeAll任务结果3";
}
));
futures.forEach(f -> {
try {
System.out.println(f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
关闭线程池
1、 shutdown()
线程池的状态变为SHUTDOWN,不会接收新的任务,但已提交的任务会执行完,此方法不会阻塞调用线程的执行
2、 shutdownNow()
线程池的状态变为STOP,不会接收新任务,会将队列中的任务返回,并用interrupt的方式中断正在执行的任务
查看线程池状态的方法
1、isShutDown()
当调用shutdown()或shutdownNow()方法后返回为true。
2、isTerminated()
当调用shutdown()方法后,并且所有提交的任务完成后,包括任务队列里面的任务返回为true。当调用shutdownNow()方法后,成功停止后返回为true。
如果线程池任务正常完成,都为false
一般可以利用shutDown()与isTerminated()来配合,实现等待线程池中的任务完成之后再去执行后面的逻辑
如下代码:
/*创建任务*/
Runnable runnable = new Runnable() {
public void run() {
// 执行任务xxxx
}
};
/*创建线程池*/
ExecutorService executorService = Executors.newFixedThreadPool(10);
long end;
long start = System.currentTimeMillis();
/*执行任务*/
for (int i=0; i<100; i++) {
executorService.execute(runnable);
}
// 关闭线程池不会接收新的任务 但已提交的任务会执行完 shutdown()此方法不会阻塞调用线程的执行
executorService.shutdown();
while (true) {
if(executorService.isTerminated()){
end = System.currentTimeMillis();
break;
}
}
System.out.println("用时"+(end-start)+"毫秒");