java线程池学习(四) —— Executors类
来源:互联网 发布:mac图片怎么保存 编辑:程序博客网 时间:2024/06/01 12:33
上一章我们介绍了ExecutorService接口,以及它的实现类ThreadPoolExecutor。
那么这里我们将介绍Executors类,它可以更进一步的简化我们的工作,直接创建一些预定义过的线程池
这个类也在java.util.concurrent包下。它有如下的几个比较常用的创建线程池的方法:
一:newFixedThreadPool
创建一个线程可重用的并且线程数固定的线程池。
nThreads - 池中的线程数
threadFactory - 创建新线程时使用的工厂
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads,ThreadFactory threadFactory)
二:newCachedThreadPool
创建一个可根据实际情况动态维持线程数的线程池,当任务到来时,如果有已经构造好的空闲线程将重用它们,不创建新的线程。
如果没有可用的空闲线程,则创建一个新线程并添加到池中。并且会终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因
此,长时间保持空闲的线程池不会使用任何资源。
threadFactory - 创建新线程时使用的工厂
public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
三:newSingleThreadExecutor
创建一个使用单个线程的 ExecutorService,以无界队列方式来运行该线程。
threadFactory - 创建新线程时使用的工厂
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
关于这3类的线程池我引用一下《thinking in java》对它们的描述:
使用FixedThreadPool,你可以一次性的预先执行代价高昂的线程分配,因而也就可以限制线程数的数量,这可以节省时间,因为你不用为每一个任务都固定的付出创建线程的开销。在事件驱动的系统中,需要线程的事件处理器,通过直接从池中获取线程,也可以如你所愿地尽快得到服务。你不会滥用可获得的资源,因为FixedThreadPool使用的Thread对象的数量是有界的。
对于CachedThreadPool,它在程序的执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的ExecutorService的首选。只有当这种方式会引发问题时,你才需要切换到FixedThreadPool。
SingleThreadExecutor就是线程数量为1的FixedThreadPool。这对于你希望在另一个线程中连续运行的任何事物(长期存活的任务)来说,都是非常有用的,例如监听进入套接字连接的任务。
=======================================================================
我们再来看一下它们的源码,比如FixedThreadPool:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
所以在本质上,就是创建了一个我们上一篇文章介绍过的ThreadPoolExecutor类。
=======================================================================
接下来我们写一个完整的,使用它们的例子:
下面给出了一个网络服务的简单结构,这里线程池中的线程作为传入的请求。它使用了预先配置的 Executors.newFixedThreadPool(int) 方法创建线程池:
class NetworkService implements Runnable { private final ServerSocket serverSocket; private final ExecutorService pool; public NetworkService(int port, int poolSize) throws IOException { serverSocket = new ServerSocket(port); pool = Executors.newFixedThreadPool(poolSize); } public void run() { // run the service try { for (;;) { pool.execute(new Handler(serverSocket.accept())); } } catch (IOException ex) { pool.shutdown(); } }}class Handler implements Runnable { private final Socket socket; Handler(Socket socket) { this.socket = socket; } public void run() { // read and service request on socket }}
============================
可以返回结果的“Runnable”
我们知道ExecutorService框架使用Runnble作为其基本的任务表示形式。但是它有很大的局限性就是它不能返回一个值,或者抛出一个受检查的异常
实际上许多需要知道执行结果的任务都需要一定的执行时间的,比如执行数据库的查询,或者从网络上获取一些资源,更或者进行一些比较复杂的计算。对于这些任务Callable是一种更好的抽象。你可以把它当成有返回值的“Runnable”,它的call()方法就相当于Runnable的run()方法。但关键是它的call()方法将返回一个值,并可能抛出一个异常。
那么怎么在ExecutorService框架中很好的使用Callable呢。这里就需要使用到Feture接口。
ExecutorService中的所有submit方法都将返回一个Future对象,从而可以将Callable提交给ExecutorService,并得到一个Future用来获得任务的执行结果或者取消任务。
public class Test {private final ExecutorService executor = Executors.newFixedThreadPool(3);public void runTheTask(){Future<String> future = executor.submit(new Callable<String>(){@Overridepublic String call() throws Exception {Thread.sleep(3000);return "result";}});try {System.out.println( future.get());} catch (InterruptedException | ExecutionException e) {}}public static void main(String args[]){Test t = new Test();t.runTheTask();}}运行后在future.get()步会阻塞 直到3秒后 返回结果“result”
上面的线程只执行了一个Callable任务。
但有某些情景下需要我们执行好几个Callable任务,并且要获得它们的返回结果,代码就变得很不好控制了
public class Test {private final ExecutorService executor = Executors.newFixedThreadPool(3);public void runTheTask(){Future<String> future1 = executor.submit(new Callable<String>(){@Overridepublic String call() throws Exception {Thread.sleep(3000);return "result1";}});Future<String> future2 = executor.submit(new Callable<String>(){@Overridepublic String call() throws Exception {Thread.sleep(3000);return "result2";}});Future<String> future3 = executor.submit(new Callable<String>(){@Overridepublic String call() throws Exception {Thread.sleep(3000);return "result3";}});try {System.out.println( future1.get());System.out.println( future2.get());System.out.println( future3.get());} catch (InterruptedException | ExecutionException e) {}}public static void main(String args[]){Test t = new Test();t.runTheTask();}}
可以看到 上面的代码非常难看而且不好控制。
幸运的是,在这种情况下我们可以使用CompletionService来实现
CompletionService将ExecutorService和BlockingQueue功能融合在了一起。
你可以将Callable任务提交给它来执行,它执行完返回的Future结果会放进BlockingQueue中。
你再用类似于队列操作的take和poll方法来获得已完成的结果。
public class Test2 {private final ExecutorService executor = Executors.newFixedThreadPool(3);public void runTasks(){CompletionService<String> completionService = new ExecutorCompletionService<String>(executor);completionService.submit(new Callable<String>(){@Overridepublic String call() throws Exception {return "result1";}});completionService.submit(new Callable<String>(){@Overridepublic String call() throws Exception {return "result2";}});completionService.submit(new Callable<String>(){@Overridepublic String call() throws Exception {return "result3";}});for(int i=0;i<3;i++){try {Future<String> f = completionService.take();System.out.println(f.get());} catch (InterruptedException e) {} catch (ExecutionException e) {}}}public static void main(String[] args) {Test2 t = new Test2();t.runTasks();}}
我们可以看到我们使用了一个ExecutorService作为参数来初始化CompletionService。多个CompletionService可以共享一个ExecutorService。因此可以创建一个对于特定计算私有,又能共享一个ExecutorService的应用。
============================
这里我们再顺带介绍一下线程池的关闭——shutdown()和shutdownNow()方法。
我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,但是它们的实现原理不同,shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。对shutdown()方法的调用可以防止新的任务被提交给这个线程池,当前线程将继续运行在shutdown()被调用之前提交的所有任务
shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。
只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。
我们加入shutdown()和shutdownNow()方法来完善一下上面这个例子:
void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // 防止新的任务被提交上来 try { // 等待当前已经存在的任务执行完 if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // 如果过了指定时间还有任务没有完成,立马停止它们 // 等待任务响应取消命令 if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } }
至此,对Executors的简单了解结束。下一篇文章我们将对上一篇文章介绍的ThreadPoolExecutor类做一下更深入的研究。
- java线程池学习(四) —— Executors类
- java学习——用 Executors创建线程池
- java线程池(四) Executors.newCachedThreadPool
- java多线程并发(二)——线程池Executors
- Java学习--线程池Executors的使用
- Java Executors(线程池)
- java Executors(线程池)
- Java Executors(线程池)
- Java Executors(线程池)
- Executors线程池--java
- Java Executors(线程池)
- java Executors线程池
- Java线程池Executors
- Java Executors(线程池)
- Java Executors(线程池)
- Java Executors(线程池)
- Java Executors(线程池)
- Java Executors(线程池)
- 真迹调试小错误
- POJ 2082 The Fewest Coins
- mac上git的安装与配置
- c#多维数组
- 责任链模式----ChainOfResponsibility
- java线程池学习(四) —— Executors类
- IOS开发-属性动画和关键帧动画的使用
- sql server数据类型详解
- UIScrollView和UIPageControl组合照片浏览滑动效果
- Java核心技术第4章(2)
- leetcode题目 合并N个排序好的链表
- matlab中hold指令、figure指令及subplot指令的使用
- ubuntu15.04编译lua源码
- 已知二叉树的前序和中序序列,不建立二叉树来输出后序序列