Java多线程之使用执行器(Executors)(Thinking in Java)

来源:互联网 发布:7英寸windows平板 编辑:程序博客网 时间:2024/06/05 08:36

JavaSE5的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。Executor允许你管理异步任务的执行,而无须显式地管理线程的生命周期。Executor在JavaSe5/6中是启动任务的优选方法。

如果程序中创建了大量的生命周期很短的线程,应该使用线程池。一个线程池中包含许多准备运行的空闲线程。将Runnable对象交给线程池,就会有一个线程调用run()方法。当run()方法退出时,线程不会死亡,而是在池中准备为下一个请求提供服务。

另一个使用线程池的理由是减少并发线程的数目。创建大量线程会大大降低性能甚至使虚拟机崩溃。如果有一个会创建许多多线程的算法,应该使用一个线程数“固定的”线程池以限制并发线程的总数。

执行器(Executors)类有许多静态工厂方法用来构建线程池,下面的表对这些方法进行了汇总:

方法描述newFixedThreadPool该池包含固定数量的线程,空闲线程会被一直保留newCachedThreadPool必要时创建新线程,空闲线程会被保留60秒newSingleThreadPool只有一个线程的线程池,该线程顺序执行每一个提交的任务(类似于Swing事件分配线程)newScheduledThreadPool用于预定执行而构建的固定线程池,替代java.util.TimernewSingleThreadScheduledPool用于预定执行而构建的单线程池

我们可以使用Executor来代替在MoreBasicThreads.java中显示地创建Thread对象,LiftOff对象知道如何运行具体的任务,与命令设计模式一样,它暴露了要执行的单一方法。ExecutorService(具有服务生命周期的Executor,例如关闭)知道如何构建恰当的上下文来执行Runnable对象。在下面的示例中,CachedThreadPool将为每个任务都创建一个线程。注意,ExecutorService对象是使用静态的Executor方法创建的,这个方法可以确定其Executor类型。

测试CachedThreadPool:

package chapter21;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * 测试CachedThreadPool  */public class CachedThreadPool {public static void main(String[] args) {//根据需要创建线程ExecutorService exec = Executors.newCachedThreadPool();for(int i =0; i < 5; i ++) {
//LiftOff参见前一篇博客
exec.execute(new LiftOff());//将任务放入线程池中执行}//当所有线程执行完成后,关闭线程池exec.shutdown();}}

运行结果:

#0(9), #3(9), #2(9), #1(9), #4(9), #3(8), #0(8), #4(8), #3(7), #0(7), #4(7), #1(8), #2(8), #1(7), #2(7), #1(6), #2(6), #3(6), #1(5), #0(6), #2(5), #0(5), #3(5), #4(6), #2(4), #1(4), #0(4), #2(3), #3(4), #0(3), #3(3), #0(2), #3(2), #0(1), #3(1), #0(LiftOff!), #3(LiftOff!), #1(3), #4(5), #2(2), #1(2), #1(1), #4(4), #2(1), #1(LiftOff!), #4(3), #4(2), #4(1), #2(LiftOff!), #4(LiftOff!), 
测试FixedThreadPool:

package chapter21;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * 测试FixedThreadPool */public class FixedThreadPoolTest {public static void main(String[] args) {//创建一个有5个线程的线程池ExecutorService exec = Executors.newFixedThreadPool(5);for(int i = 0; i < 5; i ++) {exec.execute(new LiftOff());}//当所有的任务执行完毕后,关闭线程池exec.shutdown();}}
执行结果:

#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(LiftOff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(LiftOff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(LiftOff!), #3(9), #3(8), #3(7), #3(6), #3(5), #3(4), #3(3), #3(2), #3(1), #3(LiftOff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(LiftOff!), 
SingleThreadExecutor就像是线程数量为1的FixedThreadPool。这对于你希望在另一个线程里持续运行的任何事物(长期存活的任务)来说,都是很有用的,例如监听进入的套接字连接的任务,它对于希望在线程中运行的短任务也同样很方便,例如,更新本地或远程日志的小任务或者事件分发线程。

如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队,每个任务都会在下一个任务开始之前运行结束,所有的任务将使用相同的线程。在下面的示例中,你可以看到每个任务都是按照它们被提交的顺序,并且是在下一个任务开始之前完成的。因此,SingleThreadExecutor会序列化所有交给它的任务,并会维护它自己(隐藏)的悬挂任务队列。

下面演示SingleThreadPool:

package chapter21;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * 测试SingleThreadExecutor */public class SingleThreadExecutorTest {public static void main(String[] args) {ExecutorService exec = Executors.newSingleThreadExecutor();for(int i = 0; i < 5; i ++) {exec.execute(new LiftOff());}//当所有任务执行完毕后,关闭线程池exec.shutdown();}}
运行结果:

#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(LiftOff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(LiftOff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(LiftOff!), #3(9), #3(8), #3(7), #3(6), #3(5), #3(4), #3(3), #3(2), #3(1), #3(LiftOff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(LiftOff!),