实现 Runnable 的 class 被认为是 ExecutorServices 的 'runnable task'?

A class that implements Runnable is considered to be a 'runnable task' to ExecutorServices?

我正在尝试使用 Executors 而不是同步方法来为我管理线程。但是 submit 方法采用 Runnable task 而不是真正的 Runnable target,这正是我所追求的,因为我正在启动目标 class 所在的单个线程将被实例化并 运行,像这样:

        thread = new Thread(this, "thread-process");
        thread.start();

我试着用执行者做一些等效的事情:

        this.exec = Executors.newSingleThreadExecutor();
        this.exec.submit(this);

线程似乎正在启动并且 运行 据我所知还不错,但我不确定这些是否等同?这是我在使用 Executors 时处理可运行目标线程的方式吗?

tl;博士

  • 是的,这两种方法实现了相同的目的:一些代码在后台线程上执行。
  • 使用第二个(Executors 框架)而不是显式 Thread 实例化。
  • 不要纠结于引用您的 Runnable 对象的变量名称。

详情

是的,给定一个 Runnable (an object with a run 方法)例如:

public class ReportRunnable implements Runnable {

    public void run() {
        System.out.println( "Reporting at " + Instant.now() );  // Passing: ( Runnable target , String name ).
    }

}

…然后这样做:

Runnable runnable = new ReportRunnable() ;
thread = new Thread( runnable , "thread-process" );
thread.start();

...实际上与这样做相同:

ExecutorService executorService = Executors.newSingleThreadExecutor() ;
… // You should be keeping a reference to the executor service, so that you can later shut it down gracefully.
Runnable runnable = new ReportRunnable() ;
executorService.submit( runnable ) ;

在这两种情况下,您都会立即启动一些在后台线程上执行的工作。所以从这个意义上说,它们具有相同的效果。但也有重要的区别。

了解 Executors 框架(参见 Tutorial by Oracle)的发明是为了让大多数程序员在大多数情况下不必掌握管理线程的艺术。因此,您很少需要再实例化 Thread 自己了。尽可能使用执行器框架。

一个区别是执行者服务的submit method returns a Future对象。在我们上面的代码中,我们忽略了这个返回的对象。但您可能希望捕获对该对象的引用,以便稍后查询任务的进度或完成状态。

另一个区别是您可以选择从单线程切换到使用由执行程序服务自动管理的线程池。在某些情况下,您可能希望一组任务共享同一个线程池。执行者服务使这变得非常简单。

ExecutorService executorService = Executors.newFixedThreadPool​( 3 )

;

一个很大的区别是有多种执行程序服务供您利用,提供各种功能。您可以在经过一定时间后将任务安排到 运行,而不是立即安排到 运行。您可以重复 运行 一项任务,例如每小时发送一封状态报告电子邮件。

ScheduledExecutorService ses = newSingleThreadScheduledExecutor() ;
… // You should be keeping a reference to the executor service, so that you can later shut it down gracefully.
Runnable runnable = new ReportRunnable() ;
ScheduledFuture<?> reportFuture = scheduler.scheduleAtFixedRate( runnable , 0 , 2 , TimeUnit.HOURS ) ;

另一个区别是管理线程终止,如下所述。

变量名无关紧要

你说:

But the submit method takes a Runnable task and not really a Runnable target

您似乎对变量命名感到困惑。 此处变量命名无关紧要Thread constructor Javadoc 使用 target,如我在 run 方法中的评论中复制粘贴的那样。然而,我选择使用名为 runnable 的变量。您可以选择 exportQuarterlySalesDataRunnabletasktargetpinkElephant 这样的名称。

终止线程

请注意,最终您需要终止您的线程。在您的应用 运行 期间,您可能不再需要该线程或其当前正在执行的工作。或者您的应用程序可能正在结束其 运行,在这种情况下,您必须关闭您的线程,否则它们可能会在您的应用程序停止后继续 运行 僵尸般的状态。

执行器服务使用几个 shutdown… 方法使线程终止变得容易。您可以选择中断任何当前正在完成的工作,或等到任何当前工作完成。

Lambda 语法

为了完整起见,我会提到您可以通过添加到现代 Java 的 lambda 功能使用更短的语法 Java。


强制性提示:阅读、再阅读、再阅读 Brian Goetz 等人的精彩著作 Java Concurrency in Practice