java并发编程之Executor框架

  • Executor框架结构
    ①任务:需要实现的接口:Runnable和Callable。
    ②任务的执行:任务执行核心顶层接口Executor,继承自Executor的ExecutorService接口,Executor框架的两个关键类实现了了ExecutorService(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。
    ③异步计算的结果:接口Future和实现Future接口的FutureTask类。

    Executor框架的类与接口


    Executor是框架的基础,是顶层接口,任务提交和任务执行分离。
    ThreadPoolExecutor是线程池的核心实现类,用来执行提交的任务。
    ScheduledThreadPoolExecutor实现类可以在给定的延迟后运行命令,或者定期执行命令。比Timer更灵活,功能更强大。
    Future接口和实现Future接口的FutureTask类,代表异步计算的结果。
    Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。

    Runnable与Callable联系与区别
    (1)Callable实现方法是call(),Runnable实现方法是run()
    (2)Callable的任务执行后可以返回值,而Runnable的任务无返回值
    (3)call方法可以抛出异常,run方法内部消化
    (4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可通过get阻塞方法获取执行结果。
            
    // FutureTask实现了Runnable和future,使用FutureTask包装callable
            FutureTask<String> task = new FutureTask<>(new Callable<String>(){
                @Override
                public String call() throws Exception {
                    Thread.sleep(10000);
                    return "Callable wrapper";
                }
            });
            new Thread(task).start();
            System.out.println(task.get()); //阻塞获取执行结果

    // runnable转换为Callable处理,callable静态方法可以手动设置返回值,不设置返回null
            Runnable task = new Runnable(){
                @Override
                public void run() {
                    System.out.println("runnable is running");
                }
            };
            Callable<String> callable = Executors.callable(task, "return value");
            ExecutorService myExecutor = Executors.newCachedThreadPool();
            Future<String> future = myExecutor.submit(callable);
            System.out.println(future.get());

    //上述提到submit,顺便理下submit和execute的区别
    ①execute只能提交Runnable类型的任务,而submit既能提交Runnable类型任务也能提交Callable类型任务,submit执行runnable通过future获取结果都为空值。
    ②execute方法不关心返回值,submit方法有返回值Future。
    ③excute方法会抛出异常。sumbit方法不会抛出异常。除非你调用Future.get()。

    Executor框架的主要成员
    ThreadPoolExecutor:通常使用工厂类Executors来创建三种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadPool和CachedThreadPool。这三种ThreadPoolExecutor都是调用 ThreadPoolExecutor 构造函数进行创建,区别在于参数不同。
            public static ExecutorService newFixedThreadPool(int nThreads) {
                return new ThreadPoolExecutor(nThreads, nThreads,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
            }
    FixedThreadPool的corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止。这里把keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止。

            public static ExecutorService newSingleThreadExecutor() {
                return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
            }
    SingleThreadExecutor的corePoolSize和maximumPoolSize被设置为1。其他参数与FixedThreadPool相同。SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的工作队列(队列的容量为Integer.MAX_VALUE)。SingleThreadExecutor使用无界队列作为工作队列,对线程池带来的影响与FixedThreadPool相同(OOM)。

            public static ExecutorService newCachedThreadPool() {
                return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
            }
    CachedThreadPool的corePoolSize被设置为0,即corePool为空;maximumPoolSize被设置为Integer.MAX_VALUE,即maximumPool是无界的。这里把keepAliveTime设置为60L,意味着CachedThreadPool中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但CachedThreadPool的maximumPool是无界的。这意味着,如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新线程。极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源。
     

    ScheduledThreadPoolExecutor实现了可调度的线程池功能:
    ①指定延时后执行任务
    ②周期性重复执行任务
    ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService
    ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,ScheduledThreadPoolExecutor的功能与Timer类似,但它的功能更强大、更灵活。Timer对应的是单个后台线程,而它可以在构造函数中指定多个对应的后台线程数。使用无界队列DelayQueue存储ScheduledFutureTask,所以ThreadPoolExecutor的maximumPoolSize在ScheduledThreadPoolExecutor中没有什么意义。

    ScheduledFutureTask主要包含3个成员变量。
    long型成员变量time,表示这个任务将要被执行的具体时间。
    long型成员变量sequenceNumber,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号。
    long型成员变量period,表示任务执行的间隔周期。

    DelayQueue底层实现通过优先级队列PriorityQueue,这个PriorityQueue会对队列中的ScheduledFutureTask进行排序。排序规则:time由小到大(最先到期排前),time相同按sequenceNumber由小到大(先提交排前)

    //可调度的执行者服务接口
    public interface ScheduledExecutorService extends ExecutorService {
        //指定时延后调度执行任务
        public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

        //指定时延后调度执行任务
        public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

        //指定时延后开始执行任务,以后每隔period的时长再次执行该任务
        public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,TimeUnit unit);

        //指定时延后开始执行任务,以后任务执行完成后等待delay时长,再次执行任务
        public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
    }
    schedule:用于单次调度执行任务,不做解释。
    scheduleAtFixedRate:该方法在initialDelay时长后第一次执行任务,以后每隔period时长,再次执行任务。注意,period是从任务开始执行算起的。开始执行任务后,定时器每隔period时长检查该任务是否完成,如果完成则再次启动任务,否则等该任务结束后才再次启动任务。

       scheduleWithFixDelay:该方法在initialDelay时长后第一次执行任务,以后每当任务执行完成后,等待delay时长,再次执行         任务。

       Future接口
       Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间,这个设置超时         的方法就是实现Java程序执行超时的关键。
       Future接口是一个泛型接口,严格的格式应该是Future<V>,其中V代表了Future执行的任务返回值的类型。 Future接口的    方法介绍如下:
       boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等任务结束。
       boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true。
       boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true。
       V get () throws InterruptedException, ExecutionException  等待任务执行结束,然后获得V类型的结果。                InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException
       V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException。

       FutureTask简介
       Future接口和实现Future接口的FutureTask类,代表异步计算的结果,FutureTask除了实现Future接口外,还实现了Runnable接口。因此,FutureTask可以交给Executor执行,也可以由调用线程直接执行(FutureTask.run())。根据FutureTask.run()方法被执行的时机,FutureTask可以处于下面3种状态,FutureTask的状态迁移示意图。

       FutureTask的get和cancel的执行示意图

 /**
 * Possible state transitions:
 * NEW -> COMPLETING -> NORMAL
 * NEW -> COMPLETING -> EXCEPTIONAL
 * NEW -> CANCELLED
 * NEW -> INTERRUPTING -> INTERRUPTED
 */
private volatile int state;
// 初始状态,还没有执行的,这个是在构造方法中进行赋值的
private static final int NEW = 0;
// 任务已经执行完成或者执行任务的时候发生异常,但是任务执行结果或者异常原因还没有保存到outcome字段(outcome字段用来保存任务执行结果,如果发生异常,则用来保存异常原因)的时候,状态会从NEW变更到COMPLETING。但是这个状态会时间会比较短,属于中间状态
private static final int COMPLETING  = 1;
//任务正常执行完的最终状态
private static final int NORMAL = 2;
// 任务发生异常时的最终状态
private static final int EXCEPTIONAL = 3;
// 任务取消时的状态,任务取消当并不中断,即只调用了cancel(false)方法
private static final int CANCELLED = 4;
// 用户调用了cancel(true)方法取消任务,状态会从NEW转化为INTERRUPTING,一个中间态,终态是INTERRUPTED。
private static final int INTERRUPTING = 5;
// 调用interrupt()中断任务,中断任务的最终状态
private static final int INTERRUPTED = 6;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值