【问题标题】:A class that implements Runnable is considered to be a 'runnable task' to ExecutorServices?实现 Runnable 的类被认为是 ExecutorServices 的“可运行任务”?
【发布时间】:2020-05-08 22:16:27
【问题描述】:

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

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

我尝试用 executors 做一些等效的事情:

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

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

【问题讨论】:

  • 变量名无所谓。一个 Runnable 就是一个 Runnable。
  • submit 方法:“提交 Runnable 任务以执行并返回代表该任务的 Future。Future 的 get 方法将在成功完成后返回 null。”启动方法:“可运行目标”
  • 实际上 Runnable 在这两种情况下只是在不同的线程中执行。不同的是,ExecutorService 会在 Runnable 完成后重用 Thread。

标签: java multithreading executorservice


【解决方案1】:

tl;博士

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

详情

是的,给定一个Runnable(具有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 方法返回一个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 ) ;

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

变量名无关

你说:

但是 submit 方法需要一个 Runnable 任务而不是一个真正的 Runnable 目标

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

终止线程

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

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

Lambda 语法

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


必读提示:阅读、再阅读、再阅读 Brian Goetz 等人的精彩书籍 Java Concurrency in Practice

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-18
    • 2015-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多