我正在参与「启航计划」
线程池必备知识
在开始介绍线程池之前,先来介绍下Callable
和Future
的概念,众所周知,Android
中完结多线程的办法有两种,完结Runnable
接口或许承继一个Thread
,可是这两种办法都有一个缺点:在使命履行完结之后没有回来成果,所以在Java 1.5
之后,出现了Callable
和Future
,经过他们构建的线程,能够在线程履行完结之后得到回来成果。
先来对比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
的详细完结类:
以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
也就完结了Runnable
和Future
接口
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
合适履行很多的耗时较少的操作。
效果图:
由于非中心线程数有无限个,所以不论有多少使命都能够并行履行,能够看到上述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)
只要中心线程并且不会被收回,使命行列没有巨细限制。
效果图: 由于中心线程数参数咱们传入的是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()
内部只要一个中心线程,一切使命按顺序履行 一致一切使命到一个线程中,使得这些使命不用处理线程同步问题。
效果图:
能够看到每次只能履行一个使命,也便是一切使命都是串行履行的,中心代码:
//初始化线程池
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)
中心线程是固定的,非中心线程是不固定的,非中心线程搁置时会被当即收回,主要用于履行定时使命和具有周期性的重复使命。
效果图:
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线程池的运用