我正在参与「启航计划」

线程池必备知识

在开始介绍线程池之前,先来介绍下CallableFuture的概念,众所周知,Android中完结多线程的办法有两种,完结Runnable接口或许承继一个Thread,可是这两种办法都有一个缺点:在使命履行完结之后没有回来成果,所以在Java 1.5之后,出现了CallableFuture,经过他们构建的线程,能够在线程履行完结之后得到回来成果。

先来对比Runnable 和Callable

Runnable

public interface Runnable {
    public abstract void run();  
}  

Callable

public interface Callable<V> {
  V call() throws Exception;
 }

Runnable 接口中的run()办法的回来值是void,所以Runnable无回来值;而Callable接口中的call()办法的回来值是V,也能够抛出反常,所以Callable是能够有回来值的。接着来看下Future

Future

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;
 }

Future<V>用来获取异步核算的成果,即是获取Callable<V>使命的成果

Future 补白
cancel(boolean) 测验撤销异步使命的履行。假如使命现已履行完结、现已被撤销、由于某种原因不能被撤销,则回来false;假如使命正在履行,并且mayInterruptIfRunning为true,那么会调用interrupt()测验打断使命。该办法回来成果后,isDone()总会回来true
isCancelled() 假如在使命完结前被撤销,回来true
isDone() 假如使命完结则回来true。使命完结包括正常完毕、使命被撤销、使命产生反常,都回来true
get() 获取异步使命履行成果,假如没有回来,则阻塞等候
get(long timeout, TimeUnit unit) 在给定的时刻内等候获取异步使命成果,假如超时还未获取到成果,则会抛出TimeoutException

Future<V>仅仅一个接口,还需要看Future的详细完结类:

Android中Callable、Future、FutureTask的概念以及几种线程池的使用
以FutureTask为例,详细分析下FutureTask:

public class FutureTask<V> implements RunnableFuture<V> {
  ................其他.....................
  }

咦?FutureTask并没有完结Future接口,而是完结了一个叫RunnableFuture的接口,这货从哪里蹦出来的?咱们点进去看一下:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

吆西!本来这货不只承继了Future接口,还承继了Runnable接口(PS:接口能够多承继),那么咱们的FutureTask也就完结了RunnableFuture接口

FutureTask 补白
boolean isCancelled() 同Future
boolean isDone() 同Future
boolean cancel(boolean mayInterruptIfRunning) 同Future
V get() 同Future
V get(long timeout, TimeUnit unit) 同Future
void run() 假如这个使命没有被撤销,将直接在当时线程内履行使命

FutureTask有两个构造办法:

public FutureTask(Callable<V> callable)
public FutureTask(Runnable runnable, V result)

咱们看到FutureTask初始化时不只能够传入Callable,还能够传入一个Runnable和一个”回来成果result“,这里为什么回来成果要加引号呢,来看下源码就知道了:

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

上面FutureTask()的初始化构造参数中调用了Executors.callable()

public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}
private static final class RunnableAdapter<T> implements Callable<T> {
    private final Runnable task;
    private final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
         //把传入的值直接回来
        return result;
    }
}

经过源码咱们看到,在最终的call()办法中,直接把传入的值回来了,所以FutureTask(Runnable runnable, V result)得到的仅仅预设的成果,Runnable并不会得到履行的成果。假如不需要预设的回来成果,能够像下面这样初始化:

Future<?> f = new FutureTask<Void>(runnable, null)

FutureTask的运用:

 //初始化一个线程池
 ExecutorService executor = = Executors.newSingleThreadExecutor();
 //new 一个Callable并传入FutureTask
 FutureTask<String> future =new FutureTask<>(new Callable<String>() {
     public String call() {
        //do something
       return result;
   }});
 executor.execute(future);
 //在异步使命履行期间能够做一些其他的工作
 displayOtherThings();
 //经过future.get()得到异步使命履行成果
 String result=future.get();

ExecutorService

ExecutorService承继自Executor接口,并供给了办理线程以及创建能够追踪一个或多个异步使命的发展的Future的办法。

public interface ExecutorService extends Executor {
  //无法提交新使命,可是现已提交的使命将继续履行,当履行完结后封闭线程池
  void shutdown();
  //测验停止一切正在履行的使命,暂停等候使命的处理,并回来等候履行的使命列表。
  List<Runnable> shutdownNow();
  //假如线程池现已封闭则回来true
  boolean isShutdown();
  //假如一切使命都在线程池封闭后完结,则回来true。留意,除非首先调用 shutdown 或 shutdownNow,不然 isTerminated 永不为 true。
  boolean isTerminated();
  //阻塞等候,直到一切使命在封闭恳求后完结履行,或许超时产生,或许当时线程被中止
  // 假如此履行程序停止,则回来 true;假如停止前超时了,则回来 false 
  boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
  <T> Future<T> submit(Callable<T> task);
  <T> Future<T> submit(Runnable task, T result);
  //提交一个 Runnable 使命用于履行,并回来一个表示该使命的 Future。该 Future 的 get 办法在成功 完结时将会回来 null
  Future<?> submit(Runnable task);
  <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
  //履行给定的使命,当一切使命完结或超时期满时(不论哪个首先产生),回来保持使命状况和成果的 Future 列表。
  //回来列表的一切元素的 Future.isDone() 为 true。一旦回来后,即撤销没有完结的使命。
  //留意,能够正常地或经过抛出反常来停止已完结 使命。假如此操作正在进行时修改了给定的
  // collection,则此办法的成果是不确定的。
  <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, 
             long timeout, TimeUnit unit)
        throws InterruptedException;
  <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;
  //履行给定的使命,假如在给定的超时期满前某个使命已成功完结(也便是未抛出反常),则回来其成果。
  //一旦正常或反常回来后,则撤销没有完结的使命。假如此操作正在进行时修改了给定的 collection,
  //则此办法的成果是不确定的。
  <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
 }

线程池的运用

运用线程池的优点:

  • 当履行很多异步使命时,线程池能供给更好的性能体会,由于线程池能削减每个使命的调用开支,重用存在的线程,削减对象创建、消亡的开支
  • 还能够供给绑定和办理资源 (包括履行使命时运用的线程) 的办法。有用操控最大并发线程数,进步系统资源的运用率,同时避免过多资源竞赛,避免阻塞
  • 供给定时履行、定时履行、单线程、并发数操控等功能。

Android中供给了四种线程池,四种线程池内部完结都是直接或许直接用ThreadPoolExecutor

Executors.newCachedThreadPool()

只要Integer.MAX_VALUE个非中心线程,当有使命来时,假如线程池中的线程都处于活动状况,那么会新建线程来履行,不然就会运用闲暇线程去履行,闲暇线程都会有一个超时机制,超越60秒的闲暇线程会被收回。使命行列为空集合,所以一切使命都会被当即履行,CachedThreadPool合适履行很多的耗时较少的操作。

效果图:

Android中Callable、Future、FutureTask的概念以及几种线程池的使用

由于非中心线程数有无限个,所以不论有多少使命都能够并行履行,能够看到上述5个使命一同履行,中心代码:

//初始化线程池
ExecutorService threadPool= Executors.newCachedThreadPool();
//初始化Callable
Callable<Integer> callable = new Callable<Integer>() {
    int num = 0;
    @Override
    public Integer call() throws Exception {
        while (num < 100) {
            num++;
            sendMsg(num, what);
            Thread.sleep(50);
        }
        return 100;
    }
 };
//履行线程使命
threadPool.submit(callable);

Executors.newFixedThreadPool(int nThreads)

只要中心线程并且不会被收回,使命行列没有巨细限制。

效果图:

Android中Callable、Future、FutureTask的概念以及几种线程池的使用
由于中心线程数参数咱们传入的是4,可所以看到先履行其中的4个使命,等候有使命履行完结后接着去履行第5个使命,中心代码:

//初始化线程池
ExecutorService threadPool= Executors.newFixedThreadPool(4);
//初始化Callable
Callable<Integer> callable = new Callable<Integer>() {
    int num = 0;
    @Override
    public Integer call() throws Exception {
        while (num < 100) {
            num++;
            sendMsg(num, what);
            Thread.sleep(50);
        }
        return 100;
    }
 };
//履行线程使命
threadPool.submit(callable);

Executors.newSingleThreadExecutor()

内部只要一个中心线程,一切使命按顺序履行 一致一切使命到一个线程中,使得这些使命不用处理线程同步问题。

效果图:

Android中Callable、Future、FutureTask的概念以及几种线程池的使用

能够看到每次只能履行一个使命,也便是一切使命都是串行履行的,中心代码:

//初始化线程池
ExecutorService threadPool= Executors.newSingleThreadExecutor();
//初始化Callable
Callable<Integer> callable = new Callable<Integer>() {
    int num = 0;
    @Override
    public Integer call() throws Exception {
        while (num < 100) {
            num++;
            sendMsg(num, what);
            Thread.sleep(50);
        }
        return 100;
    }
 };
//履行线程使命
threadPool.submit(callable);

Executors.newScheduledThreadPool(int corePoolSize)

中心线程是固定的,非中心线程是不固定的,非中心线程搁置时会被当即收回,主要用于履行定时使命和具有周期性的重复使命。

效果图:

Android中Callable、Future、FutureTask的概念以及几种线程池的使用
ScheduledExecutorService传入的中心线程数是4,并且是在推迟2秒之后履行的,中心代码:

//初始化线程池,中心线程数为4
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(4);
//初始化Callable
Callable<Integer> callable = new Callable<Integer>() {
    int num = 0;
    @Override
    public Integer call() throws Exception {
        while (num < 100) {
            num++;
            sendMsg(num, what);
            Thread.sleep(50);
        }
        return 100;
    }
};
//推迟2秒后履行
scheduledPool.schedule(callable, 2, TimeUnit.SECONDS);

除上述推迟履行的办法外,ScheduledExecutorService中还有下列办法:

public interface ScheduledExecutorService extends ExecutorService {
    //推迟delay(TimeUnit 为单位)之后履行Runnable 
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);
    //推迟delay(TimeUnit 为单位)之后履行Callable
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);
    //推迟initialDelay之后每period时刻履行一次Callable,循环履行
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);
     //第一次推迟initialDelay之后履行,之后在每次完结后推迟delay时刻履行下一次操作
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);
 }

示例代码

本文比如中完好代码已上传github:Android线程池的运用