前言:
上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService。
为什么要引入线程池?
1.)new Thread()的缺点
- 每次new Thread()耗费性能
- 调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。
- 不利于扩展,比如如定时执行、定期执行、线程中断
2.)采用线程池的优点
- 重用存在的线程,减少对象创建、消亡的开销,性能佳
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
- 提供定时执行、定期执行、单线程、并发数控制等功能
ExecutorService介绍
ExecutorService是一个接口,ExecutorService接口继承了Executor接口,定义了一些生命周期的方法,
public interface ExecutorService extends Executor { void shutdown();//顺次地关闭ExecutorService,停止接收新的任务,等待所有已经提交的任务执行完毕之后,关闭ExecutorService List<Runnable> shutdownNow();//阻止等待任务启动并试图停止当前正在执行的任务,停止接收新的任务,返回处于等待的任务列表 boolean isShutdown();//判断线程池是否已经关闭 boolean isTerminated();//如果关闭后所有任务都已完成,则返回 true。注意,除非首先调用 shutdown 或 shutdownNow,否则 isTerminated 永不为 true。 boolean awaitTermination(long timeout, TimeUnit unit)//等待(阻塞)直到关闭或最长等待时间或发生中断,timeout - 最长等待时间 ,unit - timeout 参数的时间单位 如果此执行程序终止,则返回 true;如果终止前超时期满,则返回 false <T> Future<T> submit(Callable<T> task);//提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。该 Future 的 get 方法在成功完成时将会返回该任务的结果。 <T> Future<T> submit(Runnable task, T result);//提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功完成时将会返回给定的结果。 Future<?> submit(Runnable task);//提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)//执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。返回列表的所有元素的 Future.isDone() 为 true。 throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)//执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。返回列表的所有元素的 Future.isDone() 为 true。 throws InterruptedException; <T> T invokeAny(Collection<? extends Callable<T>> tasks)//执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。 throws InterruptedException, ExecutionException; <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
Executor 接口
public interface Executor { void execute(Runnable command);//执行已提交的 Runnable 任务对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法 }
Executors工厂类
通过Executors提供四种线程池,newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool。
1.)newFixedThreadPool创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
示例
ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 20; i++) { Runnable syncRunnable = new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()); } }; executorService.execute(syncRunnable); }
运行结果:总共只会创建5个线程, 开始执行五个线程,当五个线程都处于活动状态,再次提交的任务都会加入队列等到其他线程运行结束,当线程处于空闲状态时会被下一个任务复用
2.)newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程
示例:
ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { Runnable syncRunnable = new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()); } }; executorService.execute(syncRunnable); }
运行结果:可以看出缓存线程池大小是不定值,可以需要创建不同数量的线程,在使用缓存型池时,先查看池中有没有以前创建的线程,如果有,就复用.如果没有,就新建新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务
3.)newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行
schedule(Runnable command,long delay, TimeUnit unit)创建并执行在给定延迟后启用的一次性操作
示例:表示从提交任务开始计时,5000毫秒后执行
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); for (int i = 0; i < 20; i++) { Runnable syncRunnable = new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()); } }; executorService.schedule(syncRunnable, 5000, TimeUnit.MILLISECONDS); }
运行结果和newFixedThreadPool类似,不同的是newScheduledThreadPool是延时一定时间之后才执行
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnitunit) 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); Runnable syncRunnable = new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()); } }; executorService.scheduleAtFixedRate(syncRunnable, 5000, 3000, TimeUnit.MILLISECONDS);
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); Runnable syncRunnable = new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; executorService.scheduleWithFixedDelay(syncRunnable, 5000, 3000, TimeUnit.MILLISECONDS);
3.)newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
示例
ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 20; i++) { Runnable syncRunnable = new Runnable() { @Override public void run() { Log.e(TAG, Thread.currentThread().getName()); } }; executorService.execute(syncRunnable); }
运行结果:只会创建一个线程,当上一个执行完之后才会执行第二个
通过ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();实现延时的单线程线程池
小结:
通过本文介绍了简单的线程池使用,也可以通过ThreadPoolExecutor定义自己的线程池,后面再做学习总结。