Java SE5的java.util.concurren包中的执行器(executor)将为你管理Thread对象,从而简化并发编程,Executor 在客户端和任务执行之间提供了一个间界层;与客户端直接执行任务不同,这个中介对象将执行任务,Executor 允许你管理异步执行的任务,而无须显示的管理线程生命周期。Executor 是Java SE5/6中启动任务的优选方案
我们可以使用Executor来代替在MoreBasicThread.java中显示的创建Thread对象。LiftOff对象知道如何运行具体的任务,与命令设计模式一样,它暴露了要执行的单一方法。ExecutorService(具有服务生命周期的Executor,例如关闭)知道如何构建恰当的上下文来执行Runnable对象。在下面的示例中,CachedThreadPool将为每个任务都创建一个线程,注意ExecutorService对象是使用静态的Executor方法创建的,这个方法可以确定其Executor类型:
import java.util.concurrent.*;
public class CachedThreadPool{
public static void main(String[]args){
ExecutorService exec=Executors.newCachedThreadPool();
//LiftOff是实现了Runnable接口的类
for(int i=0;i<5;i++){
exec.execute(new LiftOff());
exec.shutdown();
}
}
}
非常常见的情况是,单个的Executor被用来创建和管理系统中的所有任务。
对shutdown()方法的调用可以防止新任务被提交给这个Executor,当前线程(在本例中,即驱动main()的线程)将继续运行在shutdown()被调用之前提交的所有任务。这个程序将在Executor中的所有任务完成之后尽快退出。
你可以很容易的将前面示例中的CachedThreadPool替换为不同类型的Executor。FixedThreadPool使用了有限的线程集来执行所 提交的任务
import java.util.concurrent.*;
public class CachedThreadPool{
public static void main(String[]args){
ExecutorService exec=Executors.newFixedThreadPool(5);
//LiftOff是实现了Runnable接口的类
for(int i=0;i<5;i++){
exec.execute(new LiftOff());
exec.shutdown();
}
}
}
有了FixedThreadPool,你就可以一次性预先执行代价高昂的线程分配,因而也就可以限制线程的数量了。这样可以节省时间,因为你不用为每个任务都固定地付出创建线程的开销。在事件驱动的系统中,需要线程的事件处理器,通过直接从池中获取线程,也可以如你所愿地尽快得到服务。你不会滥用可获得的资源,因为FixedThreadPool使用的Thread对象的数量是由限制的。
注意,在任何线程池中,现有线程在可能的情况下都会被复用。
尽管使用CachedThreadPool,但是也应该考虑在产生线程的代码中使用FixedThreadPool。CachedThreadPool在程序过程中通常会创建与所需数量相同的线程,然后它在回收旧线程时停止创建新线程,因此它是合理的Executor的首选。只有当这种方式会引发问题时,你才需要切换到FixedThreadPool。
SingleThreadPoolExecutor顾名思义,它就像是线程数量为1的FixedThreadPool。这对于你希望在另一个另一个线程中持续运行的任何事务(长期存活的任务)来说,都是很有用的,例如监听进入的套接字连接的任务,它对于希望在线程中运行的短任务也同样很方便,例如,更新本地或远程日志的小任务,或者是事件分发线程。
如果向SingleThreadPoolExecutor提交多个任务,那么这些任务将排队,每个任务都会在下一个任务运行前结束,所有的任务将使用相同的线程池。在下面的示例中,你可以看到每个任务都是按照他们提交的顺序,并且在下一个任务开始之前完成的。因此,SingleThreadPoolExecutor会序列化所有提交给他的任务,它会维护它自己(隐藏)的悬挂任务队列。
import java.util.concurrent.*;
public class CachedThreadPool{
public static void main(String[]args){
ExecutorService exec=Executors.newSingleThreadPool();
//LiftOff是实现了Runnable接口的类
for(int i=0;i<5;i++){
exec.execute(new LiftOff());
exec.shutdown();
}
}
}
作为另一个示例,假设你有大量的线程,那他们运行的任务将使用文件系统,你可以用
SingleThreadPoolExecutor来运行这些线程,以确保任何时刻在任何线程中都只有唯一的任务在运行。在这种方式中,你不需要在共享资源上处理同步(同时不会过度使用文件系统)。有时更好的解决方案是在资源上同步,但是
SingleThreadPoolExecutor可以让你省去只是为了维持某些事物的原型而进行各种协调努力。通过序列化任务,你可以消除对序列化对象的需求