并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable
,然后在提交给一个Executor
执行,Executor
在执行时使用内部的线程池完成操作。由此,任务提交者不需要再创建管理线程,使用更方便,也减少了开销。有两种任务:Runnable
和Callable
,Callable是需要返回值的任务。Task Submitter把任务提交给Executor执行,他们之间需要一种通讯手段,这种手段的具体实现,通常叫做Future
。Future
通常包括get ,cancel,get(timeout) 等等。Future
也用于异步变同步的场景。
伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Executors
包含Executor
、ExecutorService
、ScheduledExecutorService
、ThreadFactory
和 Callable
类的工厂和实用方法。支持以下各种方法:
- 创建并返回设置有常用配置字符串的 ExecutorService 的方法。
- 创建并返回设置有常用配置字符串的 ScheduledExecutorService 的方法。
- 创建并返回“包装的”ExecutorService 方法,它通过使特定于实现的方法不可访问来禁用重新配置。
- 创建并返回 ThreadFactory 的方法,它可将新创建的线程设置为已知的状态。
- 创建并返回非闭包形式的 Callable 的方法,这样可将其用于需要 Callable 的执行方法中。
具体的方法说明如下:
-
callable(PrivilegedAction action)
返回 Callable 对象,调用它时可运行给定特权的操作并返回其结果。 -
callable(PrivilegedExceptionAction action)
返回 Callable 对象,调用它时可运行给定特权的异常操作并返回其结果。 -
callable(Runnable task)
返回 Callable 对象,调用它时可运行给定的任务并返回 null。 -
callable(Runnable task, T result)
返回 Callable 对象,调用它时可运行给定的任务并返回给定的结果。 -
defaultThreadFactory()
返回用于创建新线程的默认线程工厂。 -
newCachedThreadPool()
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 -
newCachedThreadPool(ThreadFactory threadFactory)
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程 -
newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 -
newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程 -
newScheduledThreadPool(int corePoolSize)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 -
newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 -
newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 -
newSingleThreadExecutor(ThreadFactory threadFactory)
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程,并在需要时使用提供的 ThreadFactory 创建新线程。 -
newSingleThreadScheduledExecutor()
创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 -
newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 -
privilegedCallable(Callable callable)
返回 Callable 对象,调用它时可在当前的访问控制上下文中执行给定的 callable 对象。 -
privilegedCallableUsingCurrentClassLoader(Callable callable)
返回 Callable 对象,调用它时可在当前的访问控制上下文中,使用当前上下文类加载器作为上下文类加载器来执行给定的 callable 对象。 -
privilegedThreadFactory()
返回用于创建新线程的线程工厂,这些新线程与当前线程具有相同的权限。 -
unconfigurableExecutorService(ExecutorService executor)
返回一个将所有已定义的 ExecutorService 方法委托给指定执行程序的对象,但是使用强制转换可能无法访问其他方法。 -
unconfigurableScheduledExecutorService(ScheduledExecutorService executor)
返回一个将所有已定义的 ExecutorService 方法委托给指定执行程序的对象,但是使用强制转换可能无法访问其他方法。
ScheduledExecutorServices
尽管ExecutorService
接口非常有用,但某些任务仍需要以计划方式执行,比如以确定的时间间隔或在特定时间执行给定的任务。这就是 ScheduledExecutorService
的应用范围,它扩展了ExecutorService
。
例如创建一个每隔 5 秒跳一次的 “心跳” 命令,使用ScheduledExecutorService
可以轻松实现:
1 2 3 4 5 6 7 8 9 10 | |
不用过于担心线程,不用过于担心用户希望取消心跳时会发生什么,也不用明确地将线程标记为前台或后台;只需将所有的计划细节留给ScheduledExecutorService
。如果用户希望取消心跳,scheduleAtFixedRate
调用将返回一个ScheduledFuture
实例,它不仅封装了结果(如果有),还拥有一个cancel
方法来关闭计划的操作。
下面是一个完整的示例,并行计算数组的和。
利用CompletionService
,生产者submit()
执行的任务。使用者take()
已完成的任务,并按照完成这些任务的顺序处理它们的结果 。也就是调用CompletionService
的take
方法是,会返回按完成顺序放回任务的结果,CompletionService
内部维护了一个阻塞队列BlockingQueue
,如果没有任务完成,take()
方法也会阻塞。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | |