1.线程
1.1 线程
- 线程 在Java中是一个对象,更是操作系统的资源,线程的创建和销毁都需要时间。如果 创建时间+销毁时间>执行任务时间,就很不划算了。
- Java对象占用堆内存,操作系统线程占用系统内存,根据jvm规范,一个线程默认最大栈是1M,这个栈空间需要从系统内存分配,线程过多会消耗很多的内存。
- 操作系统频繁的切换线程上下文,影响性能。
1.2 线程池
- 管理并复用线程、控制最大并发数
- 实现任务线程队列缓存策略和拒绝机制
- 实现某些与时间相关的功能,如定时任务执行、周期执行等
- 隔离线程环境,比如交易服务和搜索服务在同一台服务器上,分别开启两个线程池,交易线程的资源消耗明显要打;因此通过配置独立的线程池,将较慢的交易服务与搜索服务隔开,避免各服务线程相互影响。
1.3 概念
- 线程管理池: 用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务
- 工作线程:线程池中线程,在没有任务时处于等待状态,可以循环执行任务
- 任务接口: 每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务执行的状态等
- 任务队列: 用于存放没有处理的任务,提供缓冲机制
2. 线程池 API
2.1接口定义及实现类
- Executor: 最上层接口,定义了执行任务的方法 execute
- ExecutorService: 接口,继承了Executor,拓展了Callable、Future、关闭方法
- ScheduledExecutorService:接口, 继承了ExecutorService,增加了定时任务相关的方法
- RunnableFuture ,接口,继承自Future 接口,在线程池中,用于将Runnable 的任务包装成FutureTask,然后提交到线程池中
- ThreadPoolExecutor: 实现类,基础标准的线程池实现
- ScheduledThreadPoolExecutor: 实现类,继承了ThreadPoolExecutor,实现了ScheduledExecutorService 中相关的定时任务方法
- Executors: 工具类,里面的方法都是静态方法,用于生成ThreadPoolExecutor 的一些实例
继承关系图:
2.2 Executor 接口
/*
* @since 1.5
* @author Doug Lea
*/
public interface Executor {
void execute(Runnable command);
}
它仅提供了一个 #execute(Runnable command) 方法,用来执行已经提交的 Runnable 任务。
Executor 接口无法满足执行结果获取、当前活动线程个数、以完成任务个数等需求。
2.3 ExecutorService
ExcutorService 拓展了一些方法,为了让提交执行模式更加完善,比如提交任务的:submit、invokeAll、invokeAny,但拓展这些提交方法最终调用的还是 excute 方法
public interface ExecutorService extends Executor {
/**
* 关闭线程池,会执行完以前提交的任务,不接受继续提交新任务
*/
void shutdown();
/**
* 关闭线程池,试图停止所有正在执行的活动任务,不接受继续提交新任务
* 暂停处理正在等待的任务,并返回等待执行的任务列表
* 它和前面的方法相比,加了一个单词“now”,
* 区别在于它会去停止当前正在进行的任务
*/
List<Runnable> shutdownNow();
/**
* 如果此执行程序已关闭,则返回 true。
*/
boolean isShutdown();
/**
* 如果关闭后所有任务都已完成,则返回 true
*/
boolean isTerminated();
/**
* 请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行
*/
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
// ========== 提交任务 ==========
/**
* 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future
*/
<T> Future<T> submit(Callable<T> task);
/**
* 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
*/
<T> Future<T> submit(Runnable task, T result);
/**
* 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
*/
Future<?> submit(Runnable task);
/**
* 执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
/**
* 执行给定的任务,当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的 Future 列表
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
/**
* 执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
2.4 FutureTask
FutureTask 类通过RunnableFuture 间接实现了Runnable 接口,所以每个Runnable 通常都先包装成 FutureTask,然后调用 executor.execute(Runnable command) 将其提交给线程池
Future 接口:
public interface Future {
/**
* 试图取消对此任务的执行
* 如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。
* 当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。
* 如果任务已经启动,则 mayInterruptIfRunning 参数确定是否以中断的方式停止任务
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 如果在任务正常完成前将其取消,则返回 true
*/
boolean isCancelled();
/**
* 如果任务已完成,则返回 true
*/
boolean isDone();
/**
* 等待计算完成,然后获取其结果
*/
V get() throws InterruptedException, ExecutionException;
/**
* 最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Runnable 的 void run() 方法是没有返回值的,所以,如果需要的话,会在 submit 中指定第二个参数作为返回值:
<T> Future<T> submit(Runnable task, T result);
其实到时候会通过这两个参数,将其包装成 Callable。它和 Runnable 的区别在于 run() 没有返回值,而 Callable 的 call() 方法有返回值,同时,如果运行出现异常,call() 方法会抛出异常。
public interface Callable<V> {
V call() throws Exception;
}
2.5 AbstractExecutorService
AbstractExecutorService 抽象类派生自 ExecutorService 接口,然后在其基础上实现了几个实用的方法,这些方法提供给子类进行调用。
这个抽象类实现了 invokeAny 方法和 invokeAll 方法,这里的两个 newTaskFor 方法也比较有用,用于将任务包装成 FutureTask。定义于最上层接口 Executor中的 void execute(Runnable command) 由于不需要获取结果,不会进行 FutureTask 的包装。
需要获取结果(FutureTask),用 submit 方法,
不需要获取结果,可以用 execute 方法。
2.6 ThreadPoolExecutor
ThreadPoolExecutor 是 JDK 中的线程池实现,这个类实现了一个线程池需要的各个方法,它实现了任务提交、线程管理、监控等等方法。
Worker 是一个内部类,因为 Doug Lea 把线程池中的线程包装成了一个个 Worker,翻译成工人,就是线程池中做任务的线程。所以任务是 Runnable(内部变量名叫 task 或 command),线程是 Worker。
Worker 又用到了抽象类 AbstractQueuedSynchronizer。
2.6.1 属性
/**
* 高 3 位是 线程池状态
* 后 29 位是 工作线程个数
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 这里 COUNT_BITS 设置为 29(32-3),
//意味着前三位用于存放线程状态,后29位用于存放线程数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 000 11111111111111111111111111111
// 这里得到的是 29 个 1,也就是说线程池的最大线程数是 2^29-1=536870911
// 以我们现在计算机的实际情况,这个数量还是够用的
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
// 运算结果为 111跟29个0:111 00000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
// 000 00000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 001 00000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
// 010 00000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
// 011 00000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
// 获得线程池状态
// 将整数 c 的低 29 位修改为 0,就得到了线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 获得工作线程个数
// 将整数 c 的高 3 为修改为 0,就得到了线程池中的线程数
private static int workerCountOf(int c) { return c & CAPACITY; }
// 通过线程池状态 和 工作线程个数 得到 ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
这里定义了五种工作状态:
- RUNNING: 处于RUNNING 状态的线程池能够接收新任务,以及对新添加的任务进行处理
- SHUTDOWN: 处于SHUTDOWN 状态的先成功不可以接受新任务,但是可以对已添加的任务进行处理
- STOP: 处于STOP 状态 的线程池不接受新任务,不处理已添加的任务,中断正在执行的任务。
- TIDYIND: 当所有的任务已终止,ctl记录的“工作线程数”为0,线程池会变为 TIDYING 状态。当线程池变为TIDYING 状态会执行钩子函数terminate()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
- TERMINATED:线程池彻底终止的状态。
2.6.2 构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- corePoolSize : 线程池中核心线程的数量。当提交一个任务时,线程池会新建一个线程来执行任务,直到当前线程数等于corePoolSize。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
- maximumPoolSize: 线程池中允许的最大线程数。线程池的阻塞队列满了之后,如果还有任务提交,如果当前的线程数小于maximumPoolSize,则会新建线程来执行任务。注意,如果使用的是无界队列,该参数也就没有什么效果了。
- keepAliveTime: 线程空闲的时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁,而是继续存活一段时间:keepAliveTime。默认情况下,该参数只有在线程数大于corePoolSize时才会生效。
- unit: keepAliveTime的单位。TimeUnit
- workQueue : 用来保存等待执行的任务的阻塞队列,等待的任务必须实现Runnable接口。我们可以选择如下几种:
- threadFactory: 用于设置创建线程的工厂,通过newThread() 方法提供创建线程的功能,newThread() 方法创建的线程都是 “非守护线程” 而且 “线程优先级都是Thread.NORM_PRIORITY ”。
- handler: RejectedExecutionHandler,线程池的拒绝策略。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。当向线程池中提交任务时,如果此时线程池中的线程已经饱和了,而且阻塞队列也已经满了,则线程池会选择一种拒绝策略来处理该任务。
workQueue 阻塞队列:
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO
- LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。
- SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作,反之亦然。
- PriorityBlockingQueue:具有优先界别的阻塞队列。
线程池提供了四种拒绝策略:
- AbortPolicy:直接抛出异常,默认策略;
- CallerRunsPolicy:用调用者所在的线程来执行任务;
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy:直接丢弃任务;
- 当然我们也可以实现自己的拒绝策略,例如记录日志等等,实现RejectedExecutionHandler接口即可。
2.6.3 FixedThreadPool
可重用固定线程数的线程池,即 核心线程数 = 最大线程数,队列是无界阻塞
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2.6.4 SingleThreadExecutor
只有一个线程的线程池,即 核心线程数 = 最大线程数 = 1,队列是无界阻塞
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
2.6.5 CachedThreadPool
不限制线程数的线程池,即 核心线程数 = 0 , 最大线程数是 Integer.MAX,队列是不存储元素的 SynchronousQueue
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}