第六十八条 executor和task优先于线程

java比较早的时候,就提供了 WorkQueue WorkQueueImpl 这些类,它允许客户端将后台的异步线程加入这个队列,当不在需要这个工作队列时,客户端端可以调用一个方法,让后台线程完成了在队列中的工作后,终止自己。我们自己在用这个功能时,需要特别小心注意,防止出错。java 1.5 以后,java 平台中增加了 java.util.concurrent,这个包中包含了Executor Framework,这是一个很灵活的基于接口的任务执行工具它创建了一个在各方面都很好的队列,只需要一行这样的代码:

ExecutorService executor = Executors.newSingleThreadExecutor();里面时静态方法,封装好的线程池。
下面是为执行提交 runnable 的方法:
executor.execute(runnable);
下面是如何优雅的终止:
executor .shutdown();


    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }


看这个方法,我们知道比较关键的是 new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()) 这个对象,这是生产一个线程池,简单介绍一下里面参数的意思,这个构造方法是

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

corePoolSize    核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制,除非将 allowCoreThreadTimeOut 设置为true,否则一致存活。


maximumPoolSize 线程池所能容纳的最大线程数,超过这个数的线程将被阻塞,线程加workQueue中,等待前面线程执行完后,从queue中取出线程继续执行。


keepAliveTime  非核心线程的闲置超时时间,超过这个时间非核心线程就会被回收。


unit  指定keepAliveTime的单位,如TimeUnit.SECONDS,意思是单位为分钟。主要是对非核心线程生效,当将allowCoreThreadTimeOut设置为true时也对corePoolSize生效。


workQueue  线程池中的任务队列。常用的有三种队列,SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue。LinkedBlockingQueue在构造方法中如果没有没有传入大小限制,默认无限大

后面两个参数是用的默认值,这里只是简单介绍一下,不做过多的叙述。线程池的核心思想归纳为以下四点:

1.线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务

2.线程数量达到了corePoolSize,则将任务移入队列等待

3.队列已满,新建线程(非核心线程)执行任务

4.队列已满,总线程数又达到了maximumPoolSize,就会由(RejectedExecutionHandler)抛出异常

 

了解了上面的意思,再看看Executors中其他的静态方法。平常,我们在处理后台线程任务时,可能会有各种需求。比如想用一个线程,或者固定数目的线程,或者越多越好,这时候我们就调用 Executors 的方法,它能返回各种功能的线程池,如果我们想要另类的功能,也可以直接用 ThreadPoolExecutor 来创造出想要的线程池。

如果编写的是小程序,或者轻载的服务器,使用Executors.newCachedThreadPool通常是个不错的选择,因为它不需要配置,并且一般情况下能够正确的完成工作。但是对于大负载的服务器来说,缓存的线程池就不是很好的选择了!在缓存的线程池中,被提交的任务没有排成队列,而是直接交给线程执行。如果没有线程可用,就创建一个新的线程。如果服务器负载的太重,以致它所有的CPU都完全被占用了,当有更多的任务时,就会创建更多的线程,这样只会使情况变得更糟。因此,在大负载的产品服务器中,最好使用Executors.newFixedThreadPool,它为你提供了一个包含固定线程数目的线程池,或者为了最大限度的控制它,就直接使用ThreadPoolExecutor类。

平常开发中,我们直接调用这个类就够用了,基本没必要自己手动实现一个线程池,或者编写自己的工作队列,甚至没必要直接 new 一个 Thread,现在开启一个线程时,可以把抽象的逻辑直接写在 Runnable 或者 Callable 中,线程池可以直接执行 Runnable, 而 Callable 可以封装到 Runnable 中。Runnable 是没有返回结果的,Callable是有返回结果的,android中的 AsyncTask 就是利用线程池和 Runnable 及 Callable 实现后台逻辑的,通过用Handler来切换到主线程。耗时操作在 Callable 的 call() 方法中执行,把 Callable 对象传递到FutureTask 中,当做它的属性, FutureTask 是 Runnable 的实现类,然后用 Executor 的 execute(Runnable command) 方法执行 FutureTask, FutureTask 源码下次再分析,我们只需要知道,FutureTask 实现了 Runnable,在 run()方法中执行了 Callable 的 call() 方法,而 call() 中执行就是 AsyncTask 的 Result doInBackground(Params... params); 方法,这样,执行完了,会调用 FutureTask 的 done() 方法,然后把 run() 方法中得到的数据,通过Handler,发给UI线程执行。这段代码很有意思,大伙可以看看。

Executor Framework也有一个可以代替java.util.Timer的东西,即ScheduledThreadPoolExecutor。虽然timer使用起来更加易,但是被调度的线程池executor更加灵活。timer只用一个线程来执行任务,如果timer唯一的线程抛出未被捕获的异常,timer就会停止执行。被调度的线程池executor支持多个线程,并且优雅的从抛出未受检异常的任务中恢复。

你可能感兴趣的:(java,effective,注解,executor,As)