任务执行
大多数并发应用程序都是围绕"任务执行"来执行的:任务通常是一些抽象且离散的工作单元。
大多数服务器应用都提供了一种自然的任务边界选择方式:以独立的客户请求为边界。
串行的执行任务
显式的为任务创建线程
无限制创建线程的不足
无限制创建线程是非常危险的 理由如下
- 线程生命周期开销非常高 : 线程的创建和销毁并不是没有代价的
- 资源消耗 :活跃的线程会消耗系统资源,尤其是内存
- 稳定性 : 在可创建线程的数量上存在一个限制。如jvm上 thread太多会抛出异常
Executor 框架
任务是一组逻辑工作单元,而线程则是让任务异步执行的机制。
Executor是基于生产者消费者模式,提交任务相当于生产者(生成待完成的工作单元),执行任务相当于消费者(执行完这些工作单元)。
线程池
线程池中有一个工作者线程的概念,它的任务很简单:从工作队列中获取一个任务,执行任务,然后返回线程池等待下一个任务。
Executors 方法类 提供了一些申明线程池的方法。
- newFixedThreadPool
- newCachedThreadPool
- newSingleThreadExecutor
- newScheduledThreadPool
它们的意义我们可以很容易的从他们的名字中看出来
线程池的生命周期
我们创建了一个Executor ,但是没有讨论如何关闭它。Executor通常会创建线程来执行任务。但是JVM只有在所有非守护线程终止后才会退出。因此,如果无法正确的关闭Executor,那么jvm将无法结束。
在我们想要关闭线程池的时候,可能线程池还在工作,那么我们如何对待正在执行的任务和在队列里的任务,是平滑的对待,即执行完正在执行的任务,还是粗暴的直接停下所有。
为了解除执行服务的生命周期问题,Executor拓展了ExecutorService接口,添加了一组用于生命周期管理的方法。
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit);
// ...其它用于任务提交的便利方法。
}
线程池有三种状态 运行 关闭 终止。当我们执行shutdown()时候他就会执行我们上面说的平滑关闭过程,且状态变为关闭。这个关闭状态可能会引发误解,它其实是一种正在关闭的状态。当线程池彻底关闭后,会进入最后的终止状态。
延迟任务和周期任务
Timer类负责管理延迟任务 但是它存在问题
一个是如果一个任务执行的时间超过了定的时间间隔,那么后面的任务会受到影响。
因此我们可以选择使用ScheduledThreadPoolExecutor来做
找出可利用的并发性
Executor框架帮助指定执行策略。但是如果要使用Executor ,必须将任务表达为一个Runner。在大多数服务器应用程序中都存在一个明显的任务边界:单个客户请求。
携带结果的任务 Callable 与 Future
Runnable这个抽象有很大的局限 ,即它不能返回值。
Runnable和Callable描述的都是抽象的计算任务。这些任务都有自己的生命周期 创建 ,提交 ,开始和完成。
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, CancellationException, TimeoutException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, CancellationException, TimeoutException;
}