ExecutorService

本文翻译自http://tutorials.jenkov.com/java-util-concurrent/executorservice.html,人工翻译,仅供学习交流。

ExecutorService

java.util.concurrent.ExecutorService接口是一种能够在后台并发执行任务的异步执行机制。本文中,我将会介绍如何创建ExecutorService,如何向它提交要执行的任务,如何查看这些任务的结果以及当需要时,如何再次关闭ExecutorService。

任务委派

下面的图表演示了一个线程如何将一个任务委托给Java ExecutorService进行异步
将任务委托给ExecutorService进行异步执行的线程
一旦线程将任务委托给ExecutorService,线程继续它自己的执行,独立于该任务的执行。ExecutorService并发地执行任务,独立于提交的线程。

Java ExecutorService示例

在我们深入到ExecutorService之前,先看个样例。下面是一个简单的Java ExecutorService示例:

ExecutorService executorService = Executors.newFixedThreadPool(10);

executorService.execute(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});

executorService.shutdown();

首先,使用Executors newFixedThreadPool()工厂方法创建ExecutorService,这将创建一个线程池,其中有10个线程在执行任务。
其次,Runnable接口的匿名实现被传递给execute()方法。这将导致ExecutorService中的一个线程执行Runnable。
在本教程中,您将看到更多关于如何使用ExecutorService的示例。这个例子只是提供给你一个如何使用一个ExecutorService在后台执行任务的快速概述。

Java ExecutorService实现类

Java ExecutorService非常类似于线程池,concurrent包中ExecutorService接口的实现也是一个线程池实现。如果您想了解如何在内部实现ExecutorService接口,阅读上面的教程。
因为ExecutorService是一个接口,为了使用它,你需要它的实现类。ExecutorService在java.util.concurrent包中有以下实现:

  • ThreadPoolExecutor
  • ScheduledThreadPoolExecutor

创建ExecutorService

如何创建ExecutorService取决于您使用的实现类。你也可以使用Executors工厂类来创建ExecutorService实例。下面是一些创建ExecutorService的示例:

ExecutorService executorService1 = Executors.newSingleThreadExecutor();

ExecutorService executorService2 = Executors.newFixedThreadPool(10);

ExecutorService executorService3 = Executors.newScheduledThreadPool(10);

ExecutorService用法

有几种不同的方法可以将任务委托给ExecutorService执行:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(…)
  • invokeAll(…)
    我将在下面的部分中介绍这些方法。
Execute Runnable

ExecutorService execute(Runnable) 方法获取一个runnable对象,然后异步执行它。下面是一个使用ExecutorService执行Runnable的例子:

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.execute(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});

executorService.shutdown();

这种操作没有办法获得执行的Runnable的结果。如果需要结果,必须使用Callable(将在下面的部分中进行解释)。

Submit Runnable

ExecutorService submit(Runnable)方法也是获取Runnable实现,但会返回一个Future对象。这个Future对象可以用来检查Runnable是否已经完成执行。下面是一个Java ExecutorService submit()的例子:

Future future = executorService.submit(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});

future.get();  //returns null if the task has finished correctly.

submit()方法返回一个Java Future对象,该对象可用于检查Runnable是否完成。

Submit Callable

ExecutorService submit(Callable) 和 submit(Runnable)是相似的,除了用Callable对象代替Runnable对象。稍后将解释可调用对象和可运行对象之间的确切区别。Callable的结果可以通过submit(Callable) 方法返回的Java Future对象获得。下面是ExecutorService调用的示例:

Future future = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("Asynchronous Callable");
        return "Callable Result";
    }
});

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

上面的代码示例将输出如下:

Asynchronous Callable
future.get() = Callable Result
invokeAny()

invokeAny()方法接受一组Callable对象,调用此方法不会返回Future,但会返回一个Callable对象的结果。您无法保证得到哪个Callable结果,只有一个能完成的。
如果一个Callable完成,那么从invokeAny()返回一个结果,然后其余的Callable实例会被取消。
如果其中一个任务完成(或抛出异常),其他的Callable都被取消了。下面是一个代码示例:

ExecutorService executorService = Executors.newSingleThreadExecutor();

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

callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 1";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 2";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 3";
    }
});

String result = executorService.invokeAny(callables);

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

executorService.shutdown();

这个代码示例将打印出给定集合中的一个Callable对象返回的对象,我试着运行了几次,结果发生了变化。有时是“任务1”,有时是“任务2”。

invokeAll()

invokeAll()方法调用作为参数传递给它的集合中的所有Callable对象。invokeAll()返回一个Future对象列表,通过该列表可以获得每个Callable的执行结果。
请记住,任务可能会由于异常而结束,所以它可能没有成功。在Future上没有办法区分这两者。下面是一个代码示例:

ExecutorService executorService = Executors.newSingleThreadExecutor();

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

callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 1";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 2";
    }
});
callables.add(new Callable<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();
Runnable vs. Callable

Runnable接口与Callable接口非常相似。Runnable接口是可以由线程或 ExecutorService并发执行的任务,Callable只能由ExecutorService执行。两个接口都只有一个方法。不过,Callable和Runnable接口之间有一个微小的区别,在接口声明里更容易看到。首先是Runnable接口声明:

public interface Runnable {
    public void run();
}

下面是Callable接口声明:

public interface Callable{
    public Object call() throws Exception;
}

Runnable run()方法和Callable call()方法之间的主要区别是call()方法可以从方法调用中返回一个Object。call()和run()之间的另一个区别是call()可以抛出异常,,而run()不能(除了未检查的异常- RuntimeException的子类)。
如果需要将任务提交给Java ExecutorService,并且需要任务的结果,然后你需要让你的任务实现Callable接口。否则任务也可以只实现Runnable接口。

Cancel Task

当任务已经提交时,可以通过调用返回的Future的cancel()方法来取消提交给Java ExecutorService的任务。如果任务还没有开始,才可能取消任务。这里有一个样例:

future.cancel();

ExecutorService 关闭

当你使用完Java ExecutorService时,你应该关闭它,因此线程不会继续运行。如果你的应用程序是通过main()方法启动的,你的主线程退出你的应用程序,如果您的应用程序中有一个活动的ExexutorService,应用程序将继续运行。这个ExecutorService中的活动线程会阻止JVM关闭。

shutdown()

要终止ExecutorService内部的线程,可以调用它的shutdown()方法。ExecutorService不会立即关闭,但它将不再接受新的任务,一旦所有线程都完成了当前任务,ExecutorService就会关闭。调用shutdown()之前提交给ExecutorService的所有任务都是执行完成的。下面是一个执行Java ExecutorService关闭的示例:

executorService.shutdown();
shutdownNow()

如果您想立即关闭ExecutorService,可以调用shutdownNow()方法,这将尝试立即停止所有正在执行的任务,并跳过所有提交但未处理的任务。对于正在执行的任务没有任何保证,也许他们会停止,也许会执行到最后。这是最好的尝试。下面是调用ExecutorService shutdownNow的示例:

executorService.shutdownNow();
awaitTermination()

直到ExecutorService已经完全关闭,或者直到发生给定的超时,ExecutorService awaitterminate()方法将阻塞调用它的线程。通常在调用shutdown()或shutdownNow()之后调用awaitterminate()方法。下面是调用ExecutorService awaitTermination()的示例:

executorService.shutdown();

executorService.awaitTermination(10_000L, TimeUnit.MILLISECONDS );
下一节:Java Callable
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值