代码改变世界

Java多线程之ExecutorService

2012-09-29 17:23 by Batys, ... 阅读, ... 评论, 收藏, 编辑

  讲到Java多线程,大多数人脑海中跳出来的是Thread、Runnable、synchronized……这些是最基本的东西,虽然已经足够强大,但想要用好还真不容易。从JDK 1.5开始,增加了java.util.concurrent包,它的引入大大简化了多线程程序的开发(要感谢一下大牛Doug Lee)。 java.util.concurrent包分成了三个部分,分别是java.util.concurrent、java.util.concurrent.atomic和java.util.concurrent.lock。内容涵盖了并发集合类、线程池机制、同步互斥机制、线程安全的变量更新工具类、锁等等常用工具。 为了便于理解,本文使用一个例子来做说明,交代一下它的场景: 假设要对一套10个节点组成的环境进行检查,这个环境有两个入口点,通过节点间的依赖关系可以遍历到整个环境。依赖关系可以构成一张有向图,可能存在环。为了提高检查的效率,考虑使用多线程。

  在稍老一些的Java版本中,具体是在Java 5.0以前,启动一个任务是通过调用Thread类的start()方法来实现的,任务的提交和执行时同时进行的,如果想对任务的执行进行调度,或者想控制同时执行的线程数量就需要额外编写代码来完成。而在Java 5.0里提供了一个新的任务执行架构使我们可以很轻松地调度和控制任务的执行。这个架构主要有三个接口和其相应的类组成。这三个接口分别是:Executor、ExevutirService和ScheduledExecutorService。下面我们来具体说明。

Executor接口:是用来执行Runnable任务的,它值定义了一个方法:

  execute(Runnable command):执行Ruannable类型的任务.

ExecutorService接口:ExecutorService继承了Executor的方法,并提供了执行Callable任务和中指任务执行的服务,其定义的方法主要有:

  submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象

  invokeAll(collection of tasks):批处理任务集合,并返回一个代表这些任务的Future对象集合

  shutdown():在完成已提交的任务后关闭服务,不再接受新任务

  shutdownNow():停止所有正在执行的任务并关闭服务。

  isTerminated():测试是否所有任务都执行完毕了。

  isShutdown():测试是否该ExecutorService已被关闭

ScheduledExecutorService接口

在ExecutorService的基础上,ScheduledExecutorService提供了按时间安排执行任务的功能,它提供的方法主要有:

  schedule(task, initDelay): 安排所提交的Callable或Runnable任务在initDelay指定的时间后执行

  scheduleAtFixedRate():安排所提交的Runnable任务按指定的间隔重复执行

  scheduleWithFixedDelay():安排所提交的Runnable任务在每次执行完后,等待delay所指定的时间后重复执行

_________________________________________________________________________________________________________________________________

虽然以上提到的接口有其实现类,但是为了方便,我们建议使用Executors的工具类来得到Executor接口的具体对象,需要注意的是,Executors是一个类,不是Executor的复数形式,Executor提供了一下一些静态方法:

callable(Runnable task):将一个Runnable的任务转化成callable的任务

newSingleThreadExecutor: 产生一个ExecutorService对象,这个对象只有一个线程可用来执行任务,若任务多于一个,任务将按先后顺序执行

newCachedThreadPool(): 产生一个ExecutorService对象,这个对象带有一个线程池,线程池的大小会根据需要调整,线程执行完任务后返回线程池,供执行下一次任务使用。

newFixedThreadPool(int poolSize):产生一个ExecutorService对象,这个对象带有一个大小为poolSize的线程池,若任务数量大于poolSize,任务会被放在一个queue里顺序执行

newSingleThreadScheduledExecutor:产生一个ScheduledExecutorService对象,这个对象的线程池大小为1,若任务多于一个,任务将按先后顺序执行

newScheduledThreadPool(int poolSize): 产生一个ScheduledExecutorService对象,这个对象的线程池大小为poolSize,若任务数量大于poolSize,任务会在一个queue里等待执行

 

1、Executors 通过这个类能够获得多种线程池的实例,例如可以调用newSingleThreadExecutor()获得单线程的ExecutorService,调用newFixedThreadPool()获得固定大小线程池的ExecutorService。拿到ExecutorService可以做的事情就比较多了,最简单的是用它来执行Runnable对象,也可以执行一些实现了Callable<t>的对象。用Thread的start()方法没有返回值,如果该线程执行的方法有返回值那用ExecutorService就再好不过了,可以选择submit()、invokeAll()或者invokeAny(),根据具体情况选择合适的方法即可。

2. Atomics原子级变量 原子量级的变量,主要的类有AtomicBoolean, AtomicInteger, AotmicIntegerArray, AtomicLong, AtomicLongArray, AtomicReference ……。这些原子量级的变量主要提供两个方法: compareAndSet(expectedValue, newValue):比较当前的值是否等于expectedValue,若等于把当前值改成newValue,并返回true。若不等,返回false。 getAndSet(newValue):把当前值改为newValue,并返回改变前的值。 这些原子级变量利用了现代处理器(CPU)的硬件支持可把两步操作合为一步的功能,避免了不必要的锁定,提高了程序的运行效率。