ExecutorService 的理解与使用

来源:互联网 发布:重庆seo推广服务 编辑:程序博客网 时间:2024/06/04 18:00

ExecutorService用法详解

接口 java.util.concurrent.ExecutorService 表述了异步执行的机制,并且可以让任务在后台执行。壹個 ExecutorService 实例因此特别像壹個线程池。事实上,在 java.util.concurrent 包中的ExecutorService 的实现就是壹個线程池的实现。

ExecutorService 样例

这里有壹個简单的使用Java 实现的 ExectorService 样例:

ExecutorService executorService= Executors.newFixedThreadPool(10);

 

executorService.execute(newRunnable() {

    public void run() {

        System.out.println("Asynchronoustask");

    }

});

 

executorService.shutdown();

 

首先使用newFixedThreadPool() 工厂方法创建壹個ExecutorService ,上述代码创建了壹個可以容纳10個线程任务的线程池。其次,向 execute() 方法中传递壹個异步的Runnable 接口的实现,这样做会让ExecutorService 中的某個线程执行这個 Runnable 线程。

任务的委托(Task Delegation)

下方展示了一个线程的把任务委托异步执行的ExecutorService的示意图。

壹旦线程把任务委托给ExecutorService,该线程就会继续执行与运行任务无关的其它任务。

ExecutorService 的实现

由于 ExecutorService 只是壹個接口,你壹量需要使用它,那麽就需要提供壹個该接口的实现。ExecutorService 接口在java.util.concurrent 包中有如下实现类:

ThreadPoolExecutor

ScheduledThreadPoolExecutor

创建壹個 ExecutorService

你可以根据自己的需要来创建壹個ExecutorService ,也可以使用 Executors 工厂方法来创建壹個 ExecutorService 实例。这里有几個创建ExecutorService 的例子:

ExecutorServiceexecutorService1 = Executors.newSingleThreadExecutor();

ExecutorServiceexecutorService2 = Executors.newFixedThreadPool(10);

ExecutorServiceexecutorService3 = Executors.newScheduledThreadPool(10);

ExecutorService 使用方法

这里有几种不同的方式让你将任务委托给壹個ExecutorService:

execute(Runnable)

submit(Runnable)

submit(Callable)

invokeAny(...)

invokeAll(...)

我会在接下来的内容里把每個方法都看壹遍。

execute(Runnable)

方法 execute(Runnable) 接收壹個 java.lang.Runnable 对象作为参数,并且以异步的方式执行它。如下是壹個使用 ExecutorService 执行 Runnable 的例子:

 

ExecutorServiceexecutorService = Executors.newSingleThreadExecutor();

 

executorService.execute(newRunnable() {

    public void run() {

        System.out.println("Asynchronoustask");

    }

});

 

executorService.shutdown();

 

使用这种方式没有办法获取执行 Runnable 之后的结果,如果你希望获取运行之后的返回值,就必须使用接收 Callable 参数的 execute() 方法,后者将会在下文中提到。

submit(Runnable)

方法 submit(Runnable) 同样接收壹個 Runnable 的实现作为参数,但是会返回壹個Future 对象。这個 Future 对象可以用于判断 Runnable 是否结束执行。如下是壹個ExecutorService 的 submit() 方法的例子:

 

Future future =executorService.submit(new Runnable() {

    public void run() {

        System.out.println("Asynchronoustask");

    }

});

//如果任务结束执行则返回 null

System.out.println("future.get()="+ future.get());

submit(Callable)

方法 submit(Callable) 和方法 submit(Runnable) 比较类似,但是区别则在于它们接收不同的参数类型。Callable 的实例与 Runnable 的实例很类似,但是 Callable 的 call() 方法可以返回壹個结果。方法 Runnable.run() 则不能返回结果。

 

Callable 的返回值可以从方法 submit(Callable) 返回的 Future 对象中获取。如下是壹個 ExecutorService Callable 的样例:

 

Future future =executorService.submit(new Callable(){

    public Object call() throws Exception {

        System.out.println("AsynchronousCallable");

        return "Callable Result";

    }

});

 

System.out.println("future.get()= " + future.get());

 

上述样例代码会输出如下结果:

AsynchronousCallable

future.get() =Callable Result

inVokeAny()

方法 invokeAny() 接收壹個包含 Callable 对象的集合作为参数。调用该方法不会返回 Future 对象,而是返回集合中某壹個 Callable对象的结果,而且无法保证调用之后返回的结果是哪壹個 Callable,只知道它是这些Callable 中壹個执行结束的 Callable 对象。

 

如果壹個任务运行完毕或者抛出异常,方法会取消其它的 Callable 的执行。

 

以下是壹個样例:

ExecutorServiceexecutorService = Executors.newSingleThreadExecutor();

 

Set<Callable<String>>callables = new HashSet<Callable<String>>();

 

callables.add(newCallable<String>() {

    public String call() throws Exception {

        return "Task 1";

    }

});

callables.add(newCallable<String>() {

    public String call() throws Exception {

        return "Task 2";

    }

});

callables.add(newCallable<String>() {

    public String call() throws Exception {

        return "Task 3";

    }

});

 

String result =executorService.invokeAny(callables);

 

System.out.println("result= " + result);

 

executorService.shutdown();

 

以上样例代码会打印出在给定的集合中的某壹個Callable 的返回结果。我尝试运行了几次,结果都在改变。有时候返回结果是"Task 1",有时候是"Task2",等等。

invokeAll()

方法 invokeAll() 会调用存在于参数集合中的所有 Callable 对象,并且返回包含所有Future对象的集合,你可以通过这個返回的集合来管理每個 Callable 的执行结果。

 

需要注意的是,任务有可能因为异常而导致运行结束,所以它可能并不是真的成功运行了。但是我们没有办法通过 Future 对象来了解到这個差异。

 

以下是壹個代码样例:

ExecutorServiceexecutorService = Executors.newSingleThreadExecutor();

 

Set<Callable<String>>callables = new HashSet<Callable<String>>();

 

callables.add(newCallable<String>() {

    public String call() throws Exception {

        return "Task 1";

    }

});

callables.add(newCallable<String>() {

    public String call() throws Exception {

        return "Task 2";

    }

});

callables.add(newCallable<String>() {

    public String call() throws Exception {

        return "Task 3";

    }

});

 

List<Future<String>>futures = executorService.invokeAll(callables);

 

for(Future<String>future : futures){

    System.out.println("future.get =" + future.get());

}

 

executorService.shutdown();

ExecuteService 服务的关闭

当使用 ExecutorService 完毕之后,我们应该关闭它,这样才能保证线程不会继续保持运行状态。

 

举例来说,如果你的程序通过 main() 方法启动,并且主线程退出了你的程序,如果你还有壹個活动的 ExecutorService 存在于你的程序中,那么程序将会继续保持运行状态。存在于 ExecutorService 中的活动线程会阻止Java虚拟机关闭。

 

为了关闭在 ExecutorService 中的线程,你需要调用 shutdown() 方法。

 

ExecutorService 并不会马上关闭,而是不再接收新的任务,壹但所有的线程结束执行当前任务,ExecutorServie 才会真的关闭。所有在调用shutdown() 方法之前提交到 ExecutorService 的任务都会执行。

 

如果你希望立即关闭ExecutorService,你可以调用 shutdownNow() 方法。这個方法会尝试马上关闭所有正在执行的任务,并且跳过所有已经提交但是还没有运行的任务。但是对于正在执行的任务,是否能够成功关闭它是无法保证的,有可能他们真的被关闭掉了,也有可能它会壹直执行到任务结束。这是壹個最好的尝试。

shutdown()和shutdownNow()的区别

shutdown():执行后不再接收新任务,如果里面有任务,就执行完;通常shutdown之后调用awaitTermination(long timeout,TimeUnit unit),作用是:后者会阻塞当前线程,等待剩余任务执行完,然后继续往下执行。如果不适用await,那么shutdown之后,很可能导致剩余任务得不到执行(整个程序退出),或是执行出现异常(某些资源被释放之类)。

 

shutdownNow():执行后不再接受新任务,如果有等待任务,移出队列;有正在执行的,尝试停止之;

 

示例1:

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;  

import java.util.concurrent.Executors;  

import java.util.concurrent.Future;

 

public  class  CallableAndFuture {  

 

    /**

     * 自定义一个任务类,实现Callable接口 

     */ 

    public staticclass MyCallableClass implements Callable{  

        // 标志位  

        private intflag = 0;  

        publicMyCallableClass(int flag){  

           this.flag = flag;  

        }  

        publicString call() throws Exception{  

            if(this.flag == 0){

                // 如果flag的值为0,则立即返回  

               return "flag = 0";  

            }   

            if(this.flag == 1){

                // 如果flag的值为1,做一个无限循环  

                try{  

                   while (true) {  

                       System.out.println("looping.");  

                       Thread.sleep(2000);  

                   }  

                }catch (InterruptedException e) {  

                   System.out.println("Interrupted");  

               }  

               return "false";  

            } else{  

                //falg不为0或者1,则抛出异常  

               throw new Exception("Bad flag value!");  

            }  

        }  

    }  

      

    public staticvoid main(String[] args) {  

        // 定义3个Callable类型的任务  

       MyCallableClass task1 = new MyCallableClass(0);  

       MyCallableClass task2 = new MyCallableClass(1);  

       MyCallableClass task3 = new MyCallableClass(2);  

          

        // 创建一个执行任务的服务  

       ExecutorService es = Executors.newFixedThreadPool(3);   

        try {  

            // 提交并执行任务,任务启动时返回了一个 Future对象,  

            // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作  

            Futurefuture1 = es.submit(task1);  

            // 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行  

           System.out.println("task1: " + future1.get());  

              

            Futurefuture2 = es.submit(task2);  

            // 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环  

           Thread.sleep(5000);  

           System.out.println("task2 cancel: " + future2.cancel(true));   

              

            // 获取第三个任务的输出,因为执行第三个任务会引起异常  

            // 所以下面的语句将引起异常的抛出  

            Futurefuture3 = es.submit(task3);  

           System.out.println("task3: " + future3.get());  

        } catch(Exception e){  

            System.out.println(e.toString());  

        }  

        // 停止任务执行服务  

       es.shutdownNow();  

    }  

}

示例2:

下列方法分两个阶段关闭 ExecutorService。第一阶段调用 shutdown 拒绝传入任务,然后调用 shutdownNow(如有必要)取消所有遗留的任务: 

voidshutdownAndAwaitTermination(ExecutorService pool) {

  pool.shutdown(); // Disable new tasks from being submitted

  try {

    // Wait a while for existing tasks to terminate

    if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {

      pool.shutdownNow(); // Cancel currently executing tasks

      // Wait a while for tasks to respond to being cancelled

      if (!pool.awaitTermination(60, TimeUnit.SECONDS))

           System.err.println("Pool didnot terminate");

    }

  } catch (InterruptedException ie) {

    // (Re-)Cancel if current thread also interrupted

    pool.shutdownNow();

    // Preserve interrupt status

    Thread.currentThread().interrupt();

  }

 }

 

void shutdownAndAwaitTermination(ExecutorService pool) {  

  pool.shutdown(); // Disable new tasks from being submitted  

  try {  

    // Wait a while for existing tasks to terminate  

    if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {  

      pool.shutdownNow(); // Cancel currently executing tasks  

      // Wait a while for tasks to respond to being cancelled  

      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();  

  }  

}  

 

ExecuteService 服务的关闭

当使用 ExecutorService 完毕之后,我们应该关闭它,这样才能保证线程不会继续保持运行状态。 
举例来说,如果你的程序通过 main() 方法启动,并且主线程退出了你的程序,如果你还有壹個活动的 ExecutorService 存在于你的程序中,那么程序将会继续保持运行状态。存在于 ExecutorService 中的活动线程会阻止Java虚拟机关闭。 
为了关闭在 ExecutorService 中的线程,你需要调用 shutdown() 方法。ExecutorService 并不会马上关闭,而是不再接收新的任务,壹但所有的线程结束执行当前任务,ExecutorServie 才会真的关闭。所有在调用 shutdown() 方法之前提交到 ExecutorService 的任务都会执行。 
如果你希望立即关闭 ExecutorService,你可以调用 shutdownNow() 方法。这個方法会尝试马上关闭所有正在执行的任务,并且跳过所有已经提交但是还没有运行的任务。但是对于正在执行的任务,是否能够成功关闭它是无法保证的,有可能他们真的被关闭掉了,也有可能它会壹直执行到任务结束。这是壹個最好的尝试。 

本文英文原文链接:http://tutorials.jenkov.com/java-util-concurrent/executorservice.html#executorservice-example ,中文译文首发开源中国社区 http://my.oschina.net/bairrfhoinn/blog/177639,转载请注明原始出处。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 五险合一软件已经减员怎么办 法人社保不在投标单位怎么办 换工作单位后社保怎么办 在北京孩子没有一老一小怎么办 深户小孩怎么办社保卡 社保卡没办下来去医院住院怎么办 老年社保卡丢了怎么办 外墙掉瓷砖伤车伤人怎么办 医保卡姓名弄错了怎么办 走工伤和走社保怎么办 公司在朝阳社保在海淀怎么办 公司没缴纳个税怎么办 报个税工资报少了怎么办 医院预约单丢了怎么办 肛瘘手术后太疼怎么办 低位保肛手术后吻合口瘘怎么办 做完痔疮手术后大便困难怎么办 20岁长痔疮了怎么办 孕妇痔疮痒的难受怎么办 痔疮术后伤口不愈合怎么办 剖腹产液化伤口长的慢怎么办 内痔斑痕怎么办了能消化 油条面和稀了怎么办 解脲支原体感染怀孕怎么办 怀孕了检查出解脲支原体感染怎么办 大便是黑色的要怎么办 大人直肠给药不好意思怎么办 肛周脓肿长肉芽怎么办 二岁宝宝有直肠息肉怎么办 87岁老人得直肠息肉怎么办 做b超查出囊肿怎么办 解大便时肛门痒怎么办 生完孩子肛门痒怎么办 把达克宁软膏摸到肛门怎么办 痔疮手术后肛门痒怎么办 便秘拉完屁股疼怎么办 屁股拉屎拉破了怎么办 6岁小朋友屁眼痒怎么办 孩子屁屁偶尔痒怎么办 1岁半宝宝屁眼痒怎么办 3岁宝宝肛门痒怎么办