繁体   English   中英

Java 线程:如何为线程实现可运行的工作

[英]Java Threading: How does implementing runnable work for threading

我知道如果你想要线程,你可以扩展线程或在 java 中实现可运行到多线程。 但是为什么非要实现一个接口让java去线程呢? 使 Java 线程工作的可运行接口的重要性是什么? Java 的接口是否从某物扩展而来?

Runnable接口的唯一特殊之处在于它是Thread在其构造函数中采用的内容。 它只是一个普通的旧界面。

与大多数接口一样,关键是您正在根据合同进行编程:您同意将要运行的代码放在Runnable#run()实现中,并且Thread同意在另一个线程中运行该代码(当您创建并用它启动一个Thread )。

实际上“执行”多Thread处理的是线程(因为它与本机系统交互)。 Runnable的实现就是放置要告诉Thread运行的代码的地方。

事实上,您可以实现一个Runnable并运行它,而无需让它在单独的线程中运行:

Runnable someCode = new Runnable() {
    public void run() {
       System.out.println("I'm a runnable");
    }
};
someCode.run();

所以Runnable本身与多线程没有任何关系,它只是在对象中封装一段代码时扩展的标准接口。

在功能上,实现Runnable接口和扩展Thread类没有区别。 但是在某些情况下,可能更喜欢实现Runnable接口。 考虑一下您的类必须从其他类继承的情况,并且它还应该显示线程功能。 由于您的类不能从多个类继承(Java 不支持),因此在这种情况下您的类可以实现Runnable接口。

但是为什么非要实现一个接口让java去线程呢?

您不需要,正如您之前所说,您可以扩展 Thread 对象并实现 public void run 方法。 如果你想要一种更有条理和灵活(是的,灵活)的方法,你肯定想要使用 Runnable,原因很明显:代码可重用性。

当我说有条理时,我想说的是维护一个

Runnable doSomething = new Runnable()
{
    @Override
    public void run()
    {
        goAndDoSomethingReallyHeavyWork();
    }
};

然后为另一个线程重用相同的可运行对象,或者在另一时刻重用同一个线程(是的,您实际上可以重用一个线程),而不是将 2 个或更多线程扩展到您将使用一次的对象中。

使 Java 线程工作的可运行接口的重要性是什么?

重要的是 Thread 对象将“知道”您的 Runnable 有一个方法 run 并在必须时执行它(因此停止、暂停和其他 Thread 操作)。

Java 的接口是否从某物扩展而来?

这个问题值得我 +1 给你。 我真的很想知道,但它似乎是语言的一个特性,而不是像扩展 Object 超类的每个其他对象一样本身的产物。

我希望它有所帮助。 干杯

但是为什么非要实现一个接口让java去线程呢?

当您创建一个线程扩展类 Thread 时,您不能再扩展任何其他类(多重继承)。 另一方面,如果您使用 Runnable,如果需要,您可以获得扩展任何类的继承优势。

除了上述之外,您还可以获得内存和性能级别的好处。

ExecutorService.submit( Runnable task )

你说:

延长线程

我们不再需要直接访问Thread类来并发运行代码。 Java 5引入了 Executors 框架。 请参阅Oracle 的教程

执行程序服务管理在一个或多个后台线程上运行您的任务。 您可以从几种类型的执行程序服务中进行选择,这些服务通过Executors类实例化。

对于偶尔出现的一些短期任务,请使用由缓存线程池支持的执行程序服务。

ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit( yourRunnableObjectGoesHere ) ;

ExecutorService的工作是执行名为runcall的方法中的代码。

正如其他正确答案所解释的那样, Runnable接口的目的是它代表一个合同。 当您的代码声称实现Runnable接口时,您承诺您的代码有一个名为run的方法。

Java 编译器注意到这个承诺并检查合同是否已履行。 如果您传递的对象未能同时 (a) 声明它实现Runnable并且(b) 携带不带参数且不返回任何值的方法run ,则编译器会将这种情况标记为错误。

因此,执行程序服务要求您将任务作为实现Runnable (或Callable )接口的类的对象提交,以保证当任务到达并在后台线程上执行时,该任务有一个名为run (或call Callable )。

示例代码

这是一些示例代码。 请注意执行程序服务如何不关心您传递给它的submit方法的对象类型。 您可以传递类DogSalesReportPayroll的对象——都没有关系。 executor 服务只关心传递给submit的对象有一个名为run的方法。

package work.basil.example;

import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo
{
    public static void main ( String[] args )
    {
        Demo app = new Demo();
        app.demo();
    }

    private void demo ( )
    {
        Runnable task = new Runnable()
        {
            @Override
            public void run ( )
            {
                System.out.println( "Doing this work on a background thread. " + Instant.now() );
            }
        };

        ExecutorService executorService = null;
        try
        {
            executorService = Executors.newCachedThreadPool();
            executorService.submit( task );
            executorService.submit( task );
            executorService.submit( task );

            // Wait a moment for the background threads to do their work.
            try
            {
                Thread.sleep( Duration.ofSeconds( 2 ).toMillis() );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace();
            }
        }
        finally
        {
            if ( Objects.nonNull( executorService ) ) { executorService.shutdown(); }
            System.out.println( "Ending the main thread. " + Instant.now() );
        }
    }
}

运行时:

Doing this work on a background thread. 2020-12-20T07:16:26.119414Z
Doing this work on a background thread. 2020-12-20T07:16:26.119176Z
Doing this work on a background thread. 2020-12-20T07:16:26.119255Z
Ending the main thread. 2020-12-20T07:16:28.124384Z

Lambda 语法

如果您熟悉现代 Java 中的 lambda 语法,我们可以将其缩短为定义Runnable实现的代码行。 效果一样,只是语法不同。

package work.basil.example;

import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo
{
    public static void main ( String[] args )
    {
        Demo app = new Demo();
        app.demo();
    }

    private void demo ( )
    {
        Runnable task = ( ) -> System.out.println( "Doing this work on a background thread. " + Instant.now() );

        ExecutorService executorService = null;
        try
        {
            executorService = Executors.newCachedThreadPool();
            executorService.submit( task );
            executorService.submit( task );
            executorService.submit( task );

            // Wait a moment for the background threads to do their work.
            try
            {
                Thread.sleep( Duration.ofSeconds( 2 ).toMillis() );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace();
            }
        }
        finally
        {
            if ( Objects.nonNull( executorService ) ) { executorService.shutdown(); }
            System.out.println( "Ending the main thread. " + Instant.now() );
        }
    }
}

遗产

您询问:

Java 的接口是否从某物扩展而来?

Java 中的所有类都扩展了Object类或从Object扩展的其他类。

Java 中的接口不从任何类扩展。 请记住,接口只是一个契约,一些类可能选择做出关于具有特定名称的方法的承诺,这些方法接受特定类型的参数并返回特定类型的值。

Java 中的接口可以扩展一个或多个其他接口。 这样做只会为声称实现该接口的类所做的承诺添加更多方法。 请注意, Runnable由另外两个接口扩展: RunnableFutureRunnableScheduledFuture

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM