前言
主要讲解了
Executor
框架
正文
一. Executor框架
- 线程池: 可以通过
Executors
中的静态工厂方法之一来创建一个线程池
newFixedThreadPool
: 创建一个固定长度的线程池, 每当提交一个任务时就创建一个线程, 直到达到线程池的最大数量, 这是线程池规模将不再变化(如果某个线程发生异常终止, 那么线程池会补充一个线程)newCachedThreadPool
: 创建一个可缓存的线程池, 如果线程池的当前规模超过了处理需求时, 那么将回收空闲的线程, 而当需求增加时, 则可以添加新的线程, 线程池的规模不存在任何限制newSingleThreadExecutor
: 一个单线程的Executor
, 它创建单个工作者线程来执行任务, 如果这个线程异常结束, 会创建另一个线程来代替;newSingleThreadExecutor
能确保依照任务在队列中的顺序来执行(如:FIFO
,LIFO
, 优先级)newScheduledThreadPool
: 创建了一个固定长度的线程池, 而且以延迟或定时的方式来执行任务, 类似于Timer
Executor
生命周期: 为了解决执行服务的生命周期问题,Executor
扩展了ExecutorService
接口;ExecutorService
的生命周期有三种状态: 运行, 关闭, 已终止; 提供了如下方法:
shutdown()
: 执行平缓的关闭过程, 不再接受新任务, 同时等待已经提交的任务执行完成(包括那些还未开始执行的任务)shutdownNow()
: 执行粗暴的关闭过程, 尝试取消所有运行中的任务, 并且不再启动队列中尚未开始执行的任务awaitTermination()
: 等待ExecutorService
到达终止状态, 或者通过isTerminated()
来轮循是否已经终止
- 延迟任务与周期任务:
Timer
与ScheduleThreadPoolExecutor
Timer
: 在执行所有定时任务时只会创建一个线程, 如果某个任务执行时间过长, 那么将破坏其他TimerTask
的定时精确性;Timer
线程并不捕获异常, 因此当TimerTask
抛出未检查异常时将终止定时线程
考虑下列程序, 运行1
秒之后结束, 而不是运行6
秒; 同时还会抛出异常
public class OutOfTime {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer();
timer.schedule(new ThrowTask(), 1);
SECONDS.sleep(1);
timer.schedule(new ThrowTask(), 5);
SECONDS.sleep(5);
}
public static class ThrowTask extends TimerTask {
@Override
public void run() {
throw new RuntimeException();
}
}
}
- 携带结果的任务
Callable
与Future
:
Callable
任务主入口点(即call
)将返回一个值, 并可能抛出一个异常Future
: 表示一个任务的生命周期, 并提供了相应的方法来判断是否已经完成或取消, 以及获取任务的结果和取消任务等
二者区别参见其提供方法:
public interface Callable<V> {
V call() throws Exception;
}
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
二. 中断
-
有三个中断方法:
interrupt()
,isInterrupted()
, 静态的interrupted()
(Thread.interrupted()
) -
interrupt()
能中断目标线程, 而isInterrupt()
能返回目标线程的中断状态, 静态的interrupted()
将清除当前线程的中断状态并返回它之前的值; 这也是清除中断状态的唯一方法 -
阻塞库方法, 如:
Thread.sleep()
,Object.wait()
等, 都检查线程何时中断, 它们在响应中断时执行的操作包括: 清除中断状态, 抛出InterruptedException
-
调用
interrupt()
并不意味着立即停止目标线程正在进行的工作, 而只是传递了请求中断的消息(由线程自己去决定如何处理中断) -
中断是实现取消最合理的方式
- 响应中断:
- 传递异常
- 恢复中断状态
- 不可阻塞中断: 许多可阻塞的方法都是通过提前返回或者抛出
InterruptedException
来响应中断请求的; 但是并非所有的可阻塞方法或者阻塞机制都能响应中断, 有如下两种情况:
- 当线程执行同步的
Socket I/O
时- 等待获得内置锁而阻塞时: 但是
Lock
类中提供了lockInterruptibly
方法来允许在等待一个锁的同时仍能响应中断
三. 守护线程
线程可以分为两种: 普通线程和守护线程; 在JVM
启动时创建的所有线程中, ,除了主线程以外, 其他的线程都是守护线程(例如垃圾回收器以及其他执行辅助工作的线程); 当创建一个新线程时, 新线程将继承它的线程的守护状态
普通线程与守护线程的区别仅在于当线程退出时发生的操作, 当一个线程退出时, JVM
会检查其他正在运行的线程, 如果这些线程都是守护线程, 那么JVM
会正常退出操作, 当JVM
停止时, 所有仍然存在的守护线程都将被抛弃–既不会执行finally
代码块, 也不会执行回卷栈, 而JVM
只是直接退出