概念
Java Thread pool
represents a group of worker threads that are waiting for the job and reuse many times.
Java中创建一个线程是一个相对耗时的操作,当程序中频繁的创建和使用线程时,会产生严重的内存管理开销(significant memory management overhead)。基于这个原因,Java有了线程池的概念,在使用线程之前,创建一个线程池。当一个任务需要一个线程去运行时,程序去线程池中选择一个空闲线程去运行。当任务结束后,线程又重新放进线程池中等待下一个任务,这样就避免了频繁的创建线程,大大节省了内存管理开销。
线程池分类
fixed thread pool
: 固型线程池—-创建时指定创建的线程数,当任务使用完线程池中的空闲线程,则新任务将等待被占用的线程执行完任务。
固定长度线程池的优点 : 用Web服务器举例说明,Web服务器需要单独的线程去处理一个HTTP请求,当出现大量的HTTP请求,超过了系统能够承受的范围,那么这个Web服务器就会停止响应所有的请求。而如果使用固定长度线程池,虽然不能立刻服务请求,但系统会尽最大能力去处理。
1 | class WorkerThread implements Runnable { |
CachedThreadPool
: 缓存线程池,这样的线程池适合于有许多short-lived
任务的程序。任务执行前先查看线程池中是否有当前执行线程的缓存,如果有就resue(复用),如果没有,那么需要创建一个线程来完成当前的调用。并且这类线程池内部规定能resue(复用)的线程,空闲的时间不能超过60s,一旦超过了60s,就会被移出线程池。SingleThreadExecutor
: 单例执行器,这样的exector
保证了一个时刻只有一个线程执行。ScheduledThreadPool
: 调度型线程池,调度型线程池会根据Scheduled(任务列表)进行延迟执行,或者是进行周期性的执行.适用于一些周期性的工作。
创建线程池
- 创建固定型线程
1
ExecutorService service3 = Executors.newFixedThreadPool(10);
- 创建缓存型线程池
1
ExecutorService service2 = Executors.newCacheThreadPool();
- 创建调度型线程池
1
ExecutorService service4 = Executors.newScheduledThreadPool(10);
Future 介绍
Future表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。Future的cancel方法可以取消任务的执行,它有一布尔参数,参数为 true 表示立即中断任务的执行,参数为 false 表示允许正在运行的任务运行完成。Future的 get 方法等待计算完成,获取计算结果。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60package com.reapal.brave.main;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableAndFuture {
public static class MyCallable implements Callable{
private int flag = 0;
public MyCallable(int flag){
this.flag = flag;
}
public String call() throws Exception{
if (this.flag == 0){
return "flag = 0";
}
if (this.flag == 1){
try {
while (true) {
System.out.println("looping.");
Thread.sleep(2000);
}
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
return "false";
} else {
throw new Exception("Bad flag value!");
}
}
}
public static void main(String[] args) {
MyCallable task1 = new MyCallable(0);
MyCallable task2 = new MyCallable(1);
MyCallable task3 = new MyCallable(2);
ExecutorService es = Executors.newFixedThreadPool(3);
try {
// 提交并执行任务,任务启动时返回了一个Future对象,
// 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作
Future future1 = es.submit(task1);
// 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行
System.out.println("task1: " + future1.get());
Future future2 = es.submit(task2);
// 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环
Thread.sleep(5000);
System.out.println("task2 cancel: " + future2.cancel(true));
// 获取第三个任务的输出,因为执行第三个任务会引起异常
// 所以下面的语句将引起异常的抛出
Future future3 = es.submit(task3);
System.out.println("task3: " + future3.get());
} catch (Exception e){
System.out.println(e.toString());
}
// 停止任务执行服务
es.shutdownNow();
}
}
execute与submit区别
- 接收的参数不一样
- submit有返回值,而execute没有
- submit方便Exception处理
- execute是Executor接口中唯一定义的方法;submit是ExecutorService(该接口继承Executor)中定义的方法
关闭线程池
shutdown()
: 不会立刻停止,只是表示停止接收任务,而等待线程池中正在执行的线程结束后,才关闭线程池shutdownNow()
: 立刻关闭线程池,包括不接受任务,线程池中正在执行的任务也立刻停止。