java库Executor框架思考

大部分程序都是围绕task执行进行组织。task是work的抽象、具体单元。将work分解为task简化应用的组织。

在线程中执行task...
    第一步是识别出明显的边界,理想情况是task是独立活动,不依赖状态、结果和其他task。大部分服务器按照客户请求作为task的边界。
    
Executor框架:
    task是work的逻辑单元,线程可看作一种让task可以异步运行的机制。有界队列限制应用因为过载导致内存耗尽的,线程池在管理线程上
    也可以获得同样好处。作为Executor框架的一部分,java.uitl.concurrent包提供一个弹性线程池实现类。在java库中task执行的最原始
    抽象是Executor而不是Thread。Executor接口定义如下:
        public interface Executor {
            public void execute(Runnable command);
        }
    Executor虽然是一个简单的接口,可它是构建一个弹性而强壮的异步task执行框架的基础,支持不同的task执行策略,提供了标准的task
    执行和提交解耦方式。Executor的实现类中还添加了生命周期管理,通过hook的实现统计,应用管理和监控。Executor是基于生产-消费模式,
    提交task的角色是生产着,执行task的线程被看作消费者。使用Executor是实现消费-生产模式的最简单路径。执行策略是资源管理工具,可选
    的策略依赖可用的计算资源和服务质量。执行策略说明与task提交分离使得策略的选择更加有实践性。当你看到new Thread(task).start()
    形式的代码,需要考虑是不是要使用Executor替换。
    
线程池:
    线程池管理同质工作线程池,线程同一个工作队列仅仅绑定在一起,在队列中保存着等待执行的task,worker线程的生命周期不复杂,首先从
    队列中请求task,执行task完后等待下一个任务。在线程池中执行task的比在每个task对应一个线程更有优势。java库中提供一个带有预配置的
    弹性线程池实现,调用Executors工具类创建并使用它。
        newFixedThreadPool: 线程数量固定的线程池,任务提交到线程池时创建一个线程直到达到最大的线程数,尝试保持常量的线程;
        newCacheThreadPool: 缓存线程池提供更大的弹性,当请求量小于请求量,回收闲置的线程;当请求量增大时则新增线程。此线程池没有线程数上线;
        newSingleThreadExecutor:single-threaded执行器创建唯一的工作线程处理task,当发生异常时,创建新的线程替换它。
        newScheduledThreadPool: 固定大小线程池,支持延时和周期task执行,类似于Timer
    execute方法提交了一个task,task被添加到工作队列中,工作线程反复出队列并执行task.
    
executor生命周期:
    我们到目前为止只看到如何创建一个Executor,但不清楚如何关闭它。jvm的退出是在所有的线程终止之后,因此如果执行executor关闭操作失败将导致
    jvm无法退出。因为executor执行task采用异步方式,所以在任何时刻都无法马上清楚已提交的task的状态。Executor提供关闭服务。为了解决生命周期
    问题,ExecutorService继承Executor,添加一些生命周期管理的功能。
    public interface ExecutorService extends Executor {
        void shutdown();
        List<Runnable> shutdownNow();
        boolean isShutdown();
        boolean isTerminated();
        boolean awaitTermination(long timeout, TimeUnit unit) throws InterrupttedException;
        // additional convenience methods fors task submission
    }
    ExecutorService中涉及的生命周期总共是三个,运行、关闭和终止。ExecutorService在初始化完后处于运行状态,shutdown方法触发优雅的关闭工作,
    当没有新task被传送进来且已传递的task允许执行完成,包括还没执行的task。shutdownNow方法触发猝然关闭,它尝试取消正执行的任务并放弃
    未执行task的执行。当executorService处于关闭状态,提交的task将被拒绝。
    
延时和周期任务
    Timer设施管理延时和周期任务,然而Timer存在一些缺陷,ScheduledThreadPoolExecutor可视为它的替代。可通过构造函数或者newScheduledThreadPool
    工厂方法创建一个ScheduledThreadPoolExecutor实例。Timer按照绝对时间调度,不支持相对时间,ScheduledThreadPoolExecutor却支持相对时间。
    
寻找可利用的并发
    Executor框架使用Runnable作为它基本的task表现,Runnable有其局限性,例如无法返回结果或者抛出异常。很多task具有明显的延时计算,例如数据库
    查询,通过网络中抓取某中资源。对于这类task, Callable是更符合的抽象,它主要的入口call函数会返回结果并且会抛出异常,Executor提供许多包装
    方法将Runnable实例封装成Callable实例。task生命周期也是有限的,它们有一个清晰的起点和终点。task的生命周期的四个阶段:创建、提交、开始和结束。
    因为task可能需要执行很长时间,此时我们希望能够取消task的执行。在Executor框架中,任务被提交但没有开始的情况下可以被取消。当任务已经开始
    但是返回中断的情况下任务可能已被取消。取消一个执行完成的task没有任何负面影响。Future代表任务的生命周期且提供方法检测任务已经完成或者
    被取消、检索结果或取消task.
    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;
        V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, CancellationException, TimeoutException;
    }
    有很多手段创建描述task的Future,ExecutorService中submit函数返回一个Future,直接创建FutureTask实例。在java6的实现里,ExecutorService的实现
    AbstractExecutorService的newTaskFor函数可被覆盖,实现Future对提交的Callable和Runnable的实例化。在ThreadPoolExecutor类newTaskFor默认的实现
    代码如下:
    public <T> RunnableFuture<T> newTaskFor(Callable<T> task) {
        return new FutureTask<T>(task);
    }
    
案例:通过Future实现页面渲染
    首先将需求分解成两个任务,一个任务负责页面的渲染,另一个任务负责下载所有的照片。
    实现:创建一个下载照片Callable任务,将其提交给executorService,ExecutorService返回描述Task的Future实例,当主线程执行到需要照片数据时,它
    等待从Future.get返回。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值