“实现可运行” vs “扩展线程” 爪哇语

发布于 2024-07-13 17:44:26 字数 574 浏览 5 评论 0 原文

从我在 Java 中使用线程开始,我发现了两种编写线程的方法:

使用 implements Runnable:

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

或者,使用 extends Thread

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

这两段代码有什么显着差异吗?

From what time I've spent with threads in Java, I've found these two ways to write threads:

With implements Runnable:

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

Or, with extends Thread:

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

Is there any significant difference in these two blocks of code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

信愁 2024-07-20 17:44:26

是的:实现 Runnable 是首选方法,IMO。 您并没有真正专门化线程的行为。 你只是给它一些运行的东西。 这意味着组合哲学“更纯粹”的方法。

实际上,这意味着您可以实现 Runnable 并从另一个类扩展...并且您还可以通过 lambda 实现 Runnable从 Java 8 开始的表达式。

Yes: implements Runnable is the preferred way to do it, IMO. You're not really specialising the thread's behaviour. You're just giving it something to run. That means composition is the philosophically "purer" way to go.

In practical terms, it means you can implement Runnable and extend from another class as well... and you can also implement Runnable via a lambda expression as of Java 8.

獨角戲 2024-07-20 17:44:26

tl;dr:实现 Runnable 更好。 然而,警告很重要

一般来说,我建议使用诸如 Runnable 而不是 Thread 之类的东西,因为它允许您使您的工作与您选择的并发性松散耦合。 例如,如果您使用 Runnable 并稍后决定这实际上不需要自己的 Thread,则只需调用 threadA.run() 即可。

警告:在这里,我强烈反对使用原始线程。 我更喜欢使用 CallablesFutureTasks (来自 javadoc: “可取消的异步计算”)。 超时的集成、适当的取消和现代并发支持的线程池对我来说都比一堆原始线程有用得多。

后续:有一个FutureTask 构造函数,它允许您使用 Runnables(如果这是您最熟悉的),并且仍然可以受益于现代并发工具。 引用 javadoc

如果您不需要特定结果,请考虑使用以下形式的构造:

Future<?> f = new FutureTask<Object>(runnable, null)

因此,如果我们将其 runnable 替换为您的 threadA ,我们得到以下信息:

new FutureTask<Object>(threadA, null)

另一个让您更接近 Runnables 的选项是 ThreadPoolExecutor。 您可以使用 execute 方法传入一个 Runnable 来执行“未来某个时间的给定任务”。

如果您想尝试使用线程池,上面的代码片段将类似于以下内容(使用 Executors.newCachedThreadPool() 工厂方法):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());

tl;dr: implements Runnable is better. However, the caveat is important.

In general, I would recommend using something like Runnable rather than Thread because it allows you to keep your work only loosely coupled with your choice of concurrency. For example, if you use a Runnable and decide later on that this doesn't in fact require its own Thread, you can just call threadA.run().

Caveat: Around here, I strongly discourage the use of raw Threads. I much prefer the use of Callables and FutureTasks (From the javadoc: "A cancellable asynchronous computation"). The integration of timeouts, proper cancelling and the thread pooling of the modern concurrency support are all much more useful to me than piles of raw Threads.

Follow-up: There is a FutureTask constructor that allows you to use Runnables (if that's what you are most comfortable with) and still get the benefit of the modern concurrency tools. To quote the javadoc:

If you don't need a particular result, consider using constructions of the form:

Future<?> f = new FutureTask<Object>(runnable, null)

So, if we replace their runnable with your threadA, we get the following:

new FutureTask<Object>(threadA, null)

Another option that allows you to stay closer to Runnables is a ThreadPoolExecutor. You can use the execute method to pass in a Runnable to execute "the given task sometime in the future".

If you'd like to try using a thread pool, the code fragment above would become something like the following (using the Executors.newCachedThreadPool() factory method):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
樱花落人离去 2024-07-20 17:44:26

这个故事的寓意:

仅当您想覆盖某些行为时才继承。

或者更确切地说,它应该被理解为:

继承更少,接口更多。

Moral of the story:

Inherit only if you want to override some behavior.

Or rather it should be read as:

Inherit less, interface more.

永言不败 2024-07-20 17:44:26

如果您想实现或扩展任何其他类,那么最好是 Runnable 接口,否则,如果您不希望扩展或实现任何其他类,那么最好是 Thread 类。

最常见的区别是

在此处输入图像描述

当您扩展线程 类,之后您无法扩展您需要的任何其他类。 (如您所知,Java 不允许继承多个类)。

当您实现 Runnable 时,您可以为您的类节省空间,以便将来或现在扩展任何其他类。

  • Java 不支持多重继承,这意味着您只能扩展 Java 中的一个类,因此一旦扩展了 Thread 类,您就失去了机会,并且无法扩展或继承 Java 中的另一个类。

  • 在面向对象编程中,扩展类通常意味着添加新功能以及修改或改进行为。 如果我们不对 Thread 进行任何修改,则可以使用 Runnable 接口。

  • Runnable 接口表示可以通过普通线程或执行器或任何其他方式执行的任务。 因此,将 Task 作为 Runnable 与 Thread 进行逻辑分离是一个很好的设计决策。

  • 将任务分离为可运行意味着我们可以重用该任务,并且可以自由地通过不同的方式执行它。 因为线程一旦完成就无法重新启动。 再次就任务而言,Runnable 与 Thread 比较,Runnable 获胜。

  • Java 设计者认识到这一点,这就是为什么 Executor 接受 Runnable 作为任务,并且它们有执行这些任务的工作线程。

    Java 设计者认识到这

  • 继承所有 Thread 方法对于表示可以使用 Runnable 轻松完成的任务来说是额外的开销。

    继承所有

javarevisited.blogspot.com

提供Java 中 Thread 和 Runnable 之间的一些显着差异。 如果您知道 Thread 与 Runnable 之间的任何其他差异,请通过评论分享。 我个人在这种情况下使用 Runnable over Thread,并建议根据您的要求使用 Runnable 或 Callable 接口。

但是,显着的区别是。

当您扩展 Thread 类时,每个线程都会创建一个唯一的对象并与其关联。
当您实现 Runnable 时,它会向多个线程共享同一个对象。

If you want to implements or extends any other class then Runnable interface is most preferable, otherwise, if you do not want any other class to extend or implement then Thread class is preferable.

The most common difference is

enter image description here

When you extends Thread class, after that you can’t extend any other class which you required. (As you know, Java does not allow inheriting more than one class).

When you implements Runnable, you can save space for your class to extend any other class in the future or now.

  • Java doesn't support multiple inheritances, which means you can only extend one class in Java so once you extended Thread class you lost your chance and cannot extend or inherit another class in Java.

  • In Object-oriented programming, extending a class generally means, adding new functionality, and modifying or improving behaviors. If we are not making any modification on Thread then use Runnable interface instead.

  • Runnable interface represents a Task which can be executed by either plain Thread or Executors or any other means. so logical separation of Task as Runnable than Thread is a good design decision.

  • Separating task as Runnable means we can reuse the task and also has the liberty to execute it from different means. since you can not restart a Thread once it completes. again Runnable vs Thread for task, Runnable is winner.

  • Java designer recognizes this and that's why Executors accept Runnable as Task and they have worker thread which executes those task.

  • Inheriting all Thread methods are additional overhead just for representing a Task which can be done easily with Runnable.

Courtesy from javarevisited.blogspot.com

These were some of the notable differences between Thread and Runnable in Java. If you know any other differences on Thread vs Runnable than please share it via comments. I personally use Runnable over Thread for this scenario and recommends to use Runnable or Callable interface based on your requirement.

However, the significant difference is.

When you extends Thread class, each of your thread creates a unique object and associate with it.
When you implements Runnable, it shares the same object to multiple threads.

是你 2024-07-20 17:44:26

令我惊讶的一件事尚未被提及,那就是实现 Runnable 使您的类更加灵活。

如果您扩展线程,那么您正在执行的操作始终会在线程中。 但是,如果您实现了 Runnable,则不必如此。 您可以在线程中运行它,或者将其传递给某种执行程序服务,或者只是将其作为单线程应用程序中的任务传递(可能稍后运行,但在同一线程中)。 如果您只使用 Runnable,那么选项比您将自己绑定到 Thread 的选项要开放得多。

One thing that I'm surprised hasn't been mentioned yet is that implementing Runnable makes your class more flexible.

If you extend thread then the action you're doing is always going to be in a thread. However, if you implement Runnable it doesn't have to be. You can run it in a thread, or pass it to some kind of executor service, or just pass it around as a task within a single threaded application (maybe to be run at a later time, but within the same thread). The options are a lot more open if you just use Runnable than if you bind yourself to Thread.

泪之魂 2024-07-20 17:44:26

实际上,将 Runnable 和 Thread 进行比较并不明智。

这两者在多线程中具有依赖和关系,就像机动车的车轮和发动机的关系一样。

我想说,两步多线程只有一种方法。 让我阐述一下我的观点。

可运行:
当实现接口Runnable时,这意味着您正在创建一些在不同线程中可运行的东西。 现在创建可以在线程内运行的东西(可在线程内运行),并不意味着创建一个线程。
因此,MyRunnable 类只不过是一个具有 void run 方法的普通类。
它的对象将是一些普通对象,只有一个方法run,调用时会正常执行。 (除非我们在线程中传递对象)。

主题:
class Thread,我想说的是一个非常特殊的类,具有启动新线程的能力,它实际上通过其 start() 方法启用了多线程。

为什么不明智地进行比较?
因为我们需要它们来实现多线程。

对于多线程,我们需要两件事:

  • 可以在线程内运行的东西(可运行)。
  • Something That 可以启动一个新的线程(Thread)。

因此,从技术上和理论上讲,它们都是启动线程所必需的,一个将运行,一个将使其运行(就像车轮和引擎)机动车代码>)。

这就是为什么您无法使用 MyRunnable 启动线程,您需要将其传递给 Thread 实例。

但是仅使用Thread类就可以创建和运行线程,因为Thread类实现了Runnable,所以我们都知道Thread里面也是一个Runnable

最后,ThreadRunnable 对于多线程来说是互补的,而不是竞争对手或替代品。

Actually, It is not wise to compare Runnable and Thread with each other.

This two have a dependency and relationship in multi-threading just like Wheel and Engine relationship of motor vehicle.

I would say, there is only one way for multi-threading with two steps. Let me make my point.

Runnable:
When implementing interface Runnable it means you are creating something which is run able in a different thread. Now creating something which can run inside a thread (runnable inside a thread), doesn't mean to creating a Thread.
So the class MyRunnable is nothing but a ordinary class with a void run method.
And it's objects will be some ordinary objects with only a method run which will execute normally when called. (unless we pass the object in a thread).

Thread:
class Thread, I would say A very special class with the capability of starting a new Thread which actually enables multi-threading through its start() method.

Why not wise to compare?
Because we need both of them for multi-threading.

For Multi-threading we need two things:

  • Something that can run inside a Thread (Runnable).
  • Something That can start a new Thread (Thread).

So technically and theoretically both of them is necessary to start a thread, one will run and one will make it run (Like Wheel and Engine of motor vehicle).

That's why you can not start a thread with MyRunnable you need to pass it to a instance of Thread.

But it is possible to create and run a thread only using class Thread because Class Thread implements Runnable so we all know Thread also is a Runnable inside.

Finally Thread and Runnable are complement to each other for multithreading not competitor or replacement.

长亭外,古道边 2024-07-20 17:44:26

您应该实现 Runnable,但如果您在 Java 5 或更高版本上运行,则不应使用 new Thread 启动它,而应使用 ExecutorService 代替。 有关详细信息,请参阅:如何在 Java 中实现简单线程

You should implement Runnable, but if you are running on Java 5 or higher, you should not start it with new Thread but use an ExecutorService instead. For details see: How to implement simple threading in Java.

不醒的梦 2024-07-20 17:44:26

我不是专家,但我可以想到实现 Runnable 而不是扩展 Thread 的一个原因:Java 只支持单继承,因此只能扩展一个类。

编辑:这最初是说“实现接口需要更少的资源”。 同样,但无论哪种方式你都需要创建一个新的 Thread 实例,所以这是错误的。

I'm not an expert, but I can think of one reason to implement Runnable instead of extend Thread: Java only supports single inheritance, so you can only extend one class.

Edit: This originally said "Implementing an interface requires less resources." as well, but you need to create a new Thread instance either way, so this was wrong.

夏の忆 2024-07-20 17:44:26

我想说还有第三种方法:

public class Something {

    public void justAnotherMethod() { ... }

}

new Thread(new Runnable() {
   public void run() {
    instanceOfSomething.justAnotherMethod();
   }
}).start();

也许这受到我最近大量使用 Javascript 和 Actionscript 3 的影响,但这样你的类就不需要实现像 Runnable 这样相当模糊的接口。

I would say there is a third way:

public class Something {

    public void justAnotherMethod() { ... }

}

new Thread(new Runnable() {
   public void run() {
    instanceOfSomething.justAnotherMethod();
   }
}).start();

Maybe this is influenced a bit by my recent heavy usage of Javascript and Actionscript 3, but this way your class doesn't need to implement a pretty vague interface like Runnable.

与酒说心事 2024-07-20 17:44:26

随着 Java 8 的发布,现在有了第三种选择。

Runnable 是一个功能接口,这意味着可以使用 lambda 表达式或方法引用创建它的实例。

您的示例可以替换为:

new Thread(() -> { /* Code here */ }).start()

或者如果您想使用 ExecutorService 和方法引用:

executor.execute(runner::run)

这些不仅比您的示例短得多,而且还具有其他答案中所述的许多优点在 Thread 上使用 Runnable,例如单一责任和使用组合,因为您没有专门化线程的行为。 如果您需要的只是一个 Runnable,这种方式还可以避免创建额外的类,就像您在示例中所做的那样。

With the release of Java 8, there is now a third option.

Runnable is a functional interface, which means that instances of it can be created with lambda expressions or method references.

Your example can be replaced with:

new Thread(() -> { /* Code here */ }).start()

or if you want to use an ExecutorService and a method reference:

executor.execute(runner::run)

These are not only much shorter than your examples, but also come with many of the advantages stated in other answers of using Runnable over Thread, such as single responsibility and using composition because you're not specializing the thread's behaviour. This way also avoids creating an extra class if all you need is a Runnable as you do in your examples.

阳光下的泡沫是彩色的 2024-07-20 17:44:26

实例化接口可以在代码和线程实现之间提供更清晰的分离,因此在这种情况下我更愿意实现 Runnable。

Instantiating an interface gives a cleaner separation between your code and the implementation of threads, so I'd prefer to implement Runnable in this case.

樱娆 2024-07-20 17:44:26

扩展线程和实现 Runnable 之间的区别是:

在此处输入图像描述

Difference between Extending Thread and Implementing Runnable are:

enter image description here

半寸时光 2024-07-20 17:44:26

这里的每个人似乎都认为实现 Runnable 是正确的方法,我并不完全不同意他们的观点,但在我看来,也有扩展 Thread 的情况,事实上,您已经在代码中演示了它。

如果你实现 Runnable 那么实现 Runnable 的类无法控制线程名称,调用代码可以设置线程名称,如下所示:

new Thread(myRunnable,"WhateverNameiFeelLike");

但是如果你扩展 Thread 那么你可以在类本身中管理它(只需就像在您的示例中,您将线程命名为“ThreadB”)。 在这种情况下,您:

A)可能会为调试目的给它一个更有用的名称

B)强制该名称用于该类的所有实例(除非您忽略它是一个线程的事实并对其执行上述操作如果它是一个 Runnable 但我们在任何情况下都在这里讨论约定,所以可以忽略我认为的这种可能性)。

例如,您甚至可以获取其创建的堆栈跟踪并将其用作线程名称。 这可能看起来很奇怪,但根据代码的结构,它对于调试目的非常有用。

这看起来似乎是一件小事,但是如果您有一个非常复杂的应用程序,其中有很多线程,并且突然之间事情“停止了”(要么是由于死锁的原因,要么可能是因为网络协议中的缺陷,这会减少显而易见的 - 或其他无尽的原因)然后从Java获取堆栈转储,其中所有线程都被称为“Thread-1”,“Thread-2”,“Thread-3”并不总是很有用(这取决于您的线程如何结构化以及您是否可以仅通过堆栈跟踪来有效地判断哪个是哪个 - 如果您使用的多个线程组都运行相同的代码,则并不总是可能)。

话虽如此,您当然也可以通过创建线程类的扩展来以通用方式执行上述操作,该扩展将其名称设置为其创建调用的堆栈跟踪,然后将其与您的 Runnable 实现而不是标准 java Thread 类一起使用(见下文),但除了堆栈跟踪之外,可能还有更多上下文特定信息,这些信息在线程名称中可用于调试(例如,对它可以处理的许多队列或套接字之一的引用,在这种情况下,您可能更愿意专门针对这种情况扩展 Thread ,以便您可以让编译器强制您(或其他使用您的库的人)传递某些信息(例如有问题的队列/套接字)以在名称中使用)。

以下是通用线程的示例,其名称为调用堆栈跟踪:

public class DebuggableThread extends Thread {
    private static String getStackTrace(String name) {
        Throwable t= new Throwable("DebuggableThread-"+name);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        t.printStackTrace(ps);
        return os.toString();
    }

    public DebuggableThread(String name) {
        super(getStackTrace(name));
    }

    public static void main(String[] args) throws Exception {
        System.out.println(new Thread());
        System.out.println(new DebuggableThread("MainTest"));
    }
}

下面是比较两个名称的输出示例:

Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
    at DebuggableThread.getStackTrace(DebuggableThread.java:6)
    at DebuggableThread.<init>(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]

Everyone here seems to think that implementing Runnable is the way to go and I don't really disagree with them but there is also a case for extending Thread in my opinion, in fact you have sort of demonstrated it in your code.

If you implement Runnable then the class that implements Runnable has no control over the thread name, it is the calling code that can set the thread name, like so:

new Thread(myRunnable,"WhateverNameiFeelLike");

but if you extend Thread then you get to manage this within the class itself (just like in your example you name the thread 'ThreadB'). In this case you:

A) might give it a more useful name for debugging purposes

B) are forcing that that name be used for all instances of that class (unless you ignore the fact that it is a thread and do the above with it as if it is a Runnable but we are talking about convention here in any case so can ignore that possibility I feel).

You might even for example take a stack trace of its creation and use that as the thread name. This might seem odd but depending on how your code is structured it can be very useful for debugging purposes.

This might seem like a small thing but where you have a very complex application with a lot of threads and all of a sudden things 'have stopped' (either for reasons of deadlock or possibly because of a flaw in a network protocol which would be less obvious - or other endless reasons) then getting a stack dump from Java where all the threads are called 'Thread-1','Thread-2','Thread-3' is not always very useful (it depends on how your threads are structured and whether you can usefully tell which is which just by their stack trace - not always possible if you are using groups of multiple threads all running the same code).

Having said that you could of course also do the above in a generic way by creating an extension of the thread class which sets its name to a stack trace of its creation call and then use that with your Runnable implementations instead of the standard java Thread class (see below) but in addition to the stack trace there might be more context specific information that would be useful in the thread name for debugging (a reference to one of many queues or sockets it could processing for example in which case you might prefer to extend Thread specifically for that case so that you can have the compiler force you (or others using your libraries) to pass in certain info (e.g. the queue/socket in question) for use in the name).

Here's an example of the generic thread with the calling stack trace as its name:

public class DebuggableThread extends Thread {
    private static String getStackTrace(String name) {
        Throwable t= new Throwable("DebuggableThread-"+name);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        t.printStackTrace(ps);
        return os.toString();
    }

    public DebuggableThread(String name) {
        super(getStackTrace(name));
    }

    public static void main(String[] args) throws Exception {
        System.out.println(new Thread());
        System.out.println(new DebuggableThread("MainTest"));
    }
}

and here's a sample of the output comparing the two names:

Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
    at DebuggableThread.getStackTrace(DebuggableThread.java:6)
    at DebuggableThread.<init>(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]
故事与诗 2024-07-20 17:44:26

可运行的原因是:


  • 可运行的实现来扩展
    另一个类
  • 将代码与
    执行
  • 允许您运行您的
    可从线程池运行,
    事件线程,或以任何其他方式
    未来。

即使您现在不需要这些,将来也可能需要。 由于重写 Thread 没有任何好处,因此 Runnable 是更好的解决方案。

Runnable because:

  • Leaves more flexibility for the
    Runnable implementation to extend
    another class
  • Separates the code from
    execution
  • Allows you to run your
    runnable from a Thread Pool, the
    event thread, or in any other way in
    the future.

Even if you don't need any of this now, you may in the future. Since there is no benefit to overriding Thread, Runnable is a better solution.

耳根太软 2024-07-20 17:44:26

由于这是一个非常热门的话题,而且好的答案遍布各处并进行了深入的讨论,因此我认为将其他人的好的答案整理成更简洁的形式是合理的,以便新人可以预先轻松了解:

  1. < p>您通常扩展一个类来添加或修改功能。 因此,如果您不想覆盖任何线程行为,那么请使用 Runnable。

  2. 同样,如果您不需要继承线程方法,则可以使用 Runnable 来避免开销

  3. 单继承:如果您扩展 Thread,则无法从任何其他类扩展,因此如果您需要这样做,则必须使用 Runnable。

  4. 将领域逻辑与技术手段分开是很好的设计,从这个意义上说,最好有一个可运行的任务隔离您的任务与您的运行者< /strong>.

  5. 您可以执行同一个Runnable对象多次,但是Thread对象只能启动一次。 (也许这就是 Executors 接受 Runnables 而不是 Threads 的原因。)

  6. 如果您将任务开发为 Runnable,那么您现在和将来都可以灵活地使用它。 您可以通过执行器让它同时运行,也可以通过线程让它同时运行。 而且您仍然可以像任何其他普通类型/对象一样在同一线程中非并发地使用/调用它。

  7. 单元测试分离任务逻辑和并发方面变得更加容易。

  8. 如果您对此问题感兴趣,您可能也会对 Callable 和 Runnable 之间的区别

Since this is a very popular topic and the good answers are spread all over and dealt with in great depth, I felt it is justifiable to compile the good answers from the others into a more concise form, so newcomers have an easy overview upfront:

  1. You usually extend a class to add or modify functionality. So, if you don't want to overwrite any Thread behavior, then use Runnable.

  2. In the same light, if you don't need to inherit thread methods, you can do without that overhead by using Runnable.

  3. Single inheritance: If you extend Thread you cannot extend from any other class, so if that is what you need to do, you have to use Runnable.

  4. It is good design to separate domain logic from technical means, in that sense it is better to have a Runnable task isolating your task from your runner.

  5. You can execute the same Runnable object multiple times, a Thread object, however, can only be started once. (Maybe the reason, why Executors do accept Runnables, but not Threads.)

  6. If you develop your task as Runnable, you have all flexibility how to use it now and in the future. You can have it run concurrently via Executors but also via Thread. And you still could also use/call it non-concurrently within the same thread just as any other ordinary type/object.

  7. This makes it also easier to separate task-logic and concurrency aspects in your unit tests.

  8. If you are interested in this question, you might be also interested in the difference between Callable and Runnable.

娇纵 2024-07-20 17:44:26

Oracle 的定义和启动线程教程对此进行了讨论:

您应该使用以下哪些习语? 第一个习语使用了
Runnable对象,是比较通用的,因为Runnable对象可以
对 Thread 以外的类进行子类化。 第二个习语更容易使用
在简单的应用程序中,但受到您的任务的限制
类必须是 Thread 的后代。 本课重点讲第一点
方法,将 Runnable 任务与 Thread 对象分开
执行任务的。 这种方法不仅更加灵活,而且
它适用于所涵盖的高级线程管理 API
稍后。

换句话说,实现 Runnable 将在您的类扩展除 Thread 之外的类的情况下工作。 Java不支持多重继承。 此外,在使用某些高级线程管理 API 时,无法扩展Thread。 最好扩展Thread 的唯一场景是在将来不会更新的小型应用程序中。 实现 Runnable 几乎总是更好,因为随着项目的增长它会更加灵活。 设计更改不会产生重大影响,因为您可以在 java 中实现许多接口,但只能扩展一个类。

This is discussed in Oracle's Defining and Starting a Thread tutorial:

Which of these idioms should you use? The first idiom, which employs a
Runnable object, is more general, because the Runnable object can
subclass a class other than Thread. The second idiom is easier to use
in simple applications, but is limited by the fact that your task
class must be a descendant of Thread. This lesson focuses on the first
approach, which separates the Runnable task from the Thread object
that executes the task. Not only is this approach more flexible, but
it is applicable to the high-level thread management APIs covered
later.

In other words, implementing Runnable will work in scenarios where your class extends a class other than Thread. Java does not support multiple inheritance. Also, extending Thread will not be possible when using some of the high-level thread management APIs. The only scenario where extending Thread is preferable is in a small application that won't be subject to updates in future. It is almost always better to implement Runnable as it is more flexible as your project grows. A design change won't have a major impact as you can implement many interfaces in java, but only extend one class.

街角迷惘 2024-07-20 17:44:26

最简单的解释是,通过实现 Runnable,我们可以将同一个对象分配给多个线程,并且每个线程共享相同的对象状态和行为。

例如,假设有两个线程,thread1 将一个整数放入数组中,thread2 在数组填满时从数组中取出整数。 请注意,为了让 thread2 工作,它需要知道数组的状态,无论 thread1 是否已填满数组。

实现 Runnable 可以让您灵活地共享对象,而extends Thread 则可以为每个线程创建新对象,因此线程 1 完成的任何更新都会丢失给线程 2 。

The simplest explanation would be by implementing Runnable we can assign the same object to multiple threads and each Thread shares the same object states and behavior.

For example, suppose there are two threads, thread1 puts an integer in an array and thread2 takes integers from the array when the array is filled up. Notice that in order for thread2 to work it needs to know the state of array, whether thread1 has filled it up or not.

Implementing Runnable lets you to have this flexibility to share the object whereas extends Thread makes you to create new objects for each threads therefore any update that is done by thread1 is lost to thread2.

玩世 2024-07-20 17:44:26

将 Thread 类与 Runnable 实现分离还可以避免线程和 run() 方法之间潜在的同步问题。 单独的 Runnable 通常在引用和执行可运行代码的方式上提供了更大的灵活性。

Separating the Thread class from the Runnable implementation also avoids potential synchronization problems between the thread and the run() method. A separate Runnable generally gives greater flexibility in the way that runnable code is referenced and executed.

煮酒 2024-07-20 17:44:26

Runnable是一个接口,而Thread是一个实现该接口的类。 从设计的角度来看,任务的定义方式和执行方式之间应该有明确的分离。 前者是 Runnalbe 实现的职责,后者是 Thread 类的工作。 在大多数情况下,实现 Runnable 是正确的方法。

Runnable is an interface, while Thread is a class which implements this interface. From a design point of view, there should be a clean separation between how a task is defined and between how it is executed. The former is the responsibility of a Runnalbe implementation, and the latter is job of the Thread class. In most of the cases implementing Runnable is the right way to follow.

说谎友 2024-07-20 17:44:26

这就是 SSOLID:单一责任。

一个线程体现了一段异步执行运行上下文(如执行上下文:堆栈帧、线程ID等)代码。 理想情况下,该代码段应该是相同的实现,无论是同步还是异步

如果将它们捆绑在一个实现中,则会为生成的对象提供两个不相关的更改原因:

  1. 应用程序中的线程处理(即查询和修改执行上下文)
  2. 由代码段实现的算法(可运行部分)

如果您使用的语言支持部分类或多继承,那么您可以将每个原因隔离在其自己的超类中,但归根结底与组合两个对象相同,因为它们的功能集不重叠。 这是为了理论。

在实践中,一般来说,程序不需要具有不必要的复杂性。 如果您有一个线程正在处理一项特定任务,而无需更改该任务,则可能没有必要将这些任务设置为单独的类,并且您的代码仍然更简单。

Java的上下文中,由于该工具已经存在,因此直接从独立的Runnable类开始并传递它们可能更容易实例到Thread(或Executor)实例。 一旦使用该模式,它并不比简单的可运行线程情况更难使用(甚至阅读)。

That's the S of SOLID: Single responsibility.

A thread embodies the running context (as in execution context: stack frame, thread id, etc.) of the asynchronous execution of a piece of code. That piece of code ideally should be the same implementation, whether synchronous or asynchronous.

If you bundle them together in one implementation, you give the resulting object two unrelated causes of change:

  1. thread handling in your application (ie. querying and modifying the execution context)
  2. algorithm implemented by the piece of code (the runnable part)

If the language you use supports partial classes or multiple inheritance, then you can segregate each cause in its own super class, but it boils down to the same as composing the two objects, since their feature sets don't overlap. That's for the theory.

In practice, generally speaking, a programme does not need to carry more complexity than necessary. If you have one thread working on a specific task, without ever changing that task, there is probably no point in making the tasks separate classes, and your code remains simpler.

In the context of Java, since the facility is already there, it is probably easier to start directly with stand alone Runnable classes, and pass their instances to Thread (or Executor) instances. Once used to that pattern, it is not harder to use (or even read) than the simple runnable thread case.

狼性发作 2024-07-20 17:44:26

您想要实现接口而不是扩展基类的原因之一是您已经在扩展一些其他类。 您只能扩展一个类,但可以实现任意数量的接口。

如果扩展 Thread,则基本上会阻止“this”之外的任何其他线程执行您的逻辑。 如果您只想某个线程执行您的逻辑,最好只实现 Runnable。

One reason you'd want to implement an interface rather than extend a base class is that you are already extending some other class. You can only extend one class, but you can implement any number of interfaces.

If you extend Thread, you're basically preventing your logic to be executed by any other thread than 'this'. If you only want some thread to execute your logic, it's better to just implement Runnable.

灵芸 2024-07-20 17:44:26

如果您使用 runnable,您可以节省空间以扩展到任何其他类。

if you use runnable you can save the space to extend to any of your other class.

独享拥抱 2024-07-20 17:44:26

我们能否重新审视一下我们希望我们的类表现得像一个Thread 的基本原因?
没有任何理由,我们只是想执行一个任务,很可能是异步模式,这恰恰意味着任务的执行必须从我们的主线程分支,并且主线程如果提前完成,可能会也可能不会等待对于分支路径(任务)。

如果这就是全部目的,那么我在哪里看到需要专门的线程。 这可以通过从系统线程池中获取一个原始线程并将其分配给我们的任务(可能是我们类的实例)来完成,仅此而已。

因此,让我们遵循 OOP 概念并编写我们需要的类型的类。 做事的方法有很多种,重要的是用正确的方法做事。

我们需要一个任务,因此编写一个可以在线程上运行的任务定义。 所以使用Runnable。

永远记住 implements 专门用于传递行为,而 extends 用于传递功能/属性。

我们不想要线程的属性,而是希望我们的类充当可以运行的任务。

Can we re-visit the basic reason we wanted our class to behave as a Thread?
There is no reason at all, we just wanted to execute a task, most likely in an asynchronous mode, which precisely means that the execution of the task must branch from our main thread and the main thread if finishes early, may or may not wait for the branched path(task).

If this is the whole purpose, then where do I see the need of a specialized Thread. This can be accomplished by picking up a RAW Thread from the System's Thread Pool and assigning it our task (may be an instance of our class) and that is it.

So let us obey the OOPs concept and write a class of the type we need. There are many ways to do things, doing it in the right way matters.

We need a task, so write a task definition which can be run on a Thread. So use Runnable.

Always remember implements is specially used to impart a behaviour and extends is used to impart a feature/property.

We do not want the thread's property, instead we want our class to behave as a task which can be run.

秋叶绚丽 2024-07-20 17:44:26

如果我没记错的话,它或多或少类似于

接口和抽象类有什么区别?

extends 建立“Is A”关系 & 接口提供“拥有”功能。

首选 实现 Runnable

  1. 如果您不必扩展 Thread 类并修改 Thread API 默认实现
  2. 如果您正在执行即发即忘命令
  3. 如果您已经在扩展另一个类

首选“扩展线程”:

  1. 如果您必须覆盖其中任何一个Thread 方法如 oracle 文档页中列出

一般,您不需要覆盖线程行为。 因此,大多数时候 implements Runnable 是首选。

如果您需要对此 Thread API 进行一些自定义,您可以扩展 Thread 并修改实现

Thread(ThreadGroup group, Runnable target, String name, long stackSize)

分配一个新的 Thread 对象,使其以 target 作为其运行对象,以指定的名称作为其名称,并属于 group 引用的线程组,并且具有指定的堆栈大小。

另一方面,使用高级 ExecutorServiceThreadPoolExecutorService API 可以提供更大的灵活性和控制力。

看看这个SE问题:

ExecutorService vs Casual Thread Spawner

If I am not wrong, it's more or less similar to

What is the difference between an interface and abstract class?

extends establishes "Is A" relation & interface provides "Has a" capability.

Prefer implements Runnable :

  1. If you don't have to extend Thread class and modify Thread API default implementation
  2. If you are executing a fire and forget command
  3. If You are already extending another class

Prefer "extends Thread" :

  1. If you have to override any of these Thread methods as listed in oracle documentation page

Generally you don't need to override Thread behaviour. So implements Runnable is preferred for most of the times.

If you need some customization for this Thread API, you can extend Thread and modify implementation

Thread(ThreadGroup group, Runnable target, String name, long stackSize)

Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group, and has the specified stack size.

On a different note, using advanced ExecutorService or ThreadPoolExecutorService API provides more flexibility and control.

Have a look at this SE Question:

ExecutorService vs Casual Thread Spawner

心病无药医 2024-07-20 17:44:26

是的,
如果调用 ThreadA 类,则不需要调用 start 方法,而 run 方法只需在调用 ThreadA 类后调用即可。
但是如果使用ThreadB调用则需要启动线程来调用run方法。
如果您还有什么帮助,请回复我。

Yes,
If you call ThreadA call , then not need to call the start method and run method is call after call the ThreadA class only.
But If use the ThreadB call then need to necessary the start thread for call run method.
If you have any more help, reply me.

白首有我共你 2024-07-20 17:44:26

我发现出于上述所有原因,使用 Runnable 是最有用的,但有时我喜欢扩展 Thread,这样我就可以创建自己的线程停止方法并直接在我创建的线程上调用它。

I find it is most useful to use Runnable for all the reasons mentioned, but sometimes I like to extend Thread so I can create my own thread stopping method and call it directly on the thread I have created.

我三岁 2024-07-20 17:44:26

Java 不支持多重继承,因此如果扩展 Thread 类,则不会扩展其他类。

例如:如果您创建一个小程序,那么它必须扩展 Applet 类,因此这里创建线程的唯一方法是实现 Runnable 接口

Java does not support multiple inheritence so if you extends Thread class then no other class will be extended.

For Example: If you create an applet then it must extends Applet class so here the only way to create thread is by implementing Runnable interface

傲影 2024-07-20 17:44:26

线程和可运行线程之间的区别
如果我们使用 Thread 类创建线程,则线程数等于我们创建的对象数。
如果我们通过实现 runnable 接口来创建线程,那么我们可以使用单个对象来创建多个线程。因此单个对象由多个线程共享。因此,它将占用更少的内存。

因此,如果我们的数据不敏感,则取决于要求。 所以它可以在多个Thread之间共享,我们可以使用Runnable接口。

Difference between Thread and runnable
.If we are creating Thread using Thread class then Number of thread equal to number of object we created .
If we are creating thread by implementing the runnable interface then we can use single object for creating multiple thread.So single object is shared by multiple Thread.So it will take less memory

So depending upon the requirement if our data is not senstive. So It can be shared between multiple Thread we can used Runnable interface.

野鹿林 2024-07-20 17:44:26

在这里添加我的两分钱 -
尽可能使用实现 Runnable 。 以下是关于为什么不应该使用的两个警告
扩展Threads

  1. 理想情况下,你永远不应该扩展Thread类; Thread 类应设为final
    至少它的方法如thread.getId()
    请参阅讨论与扩展Thread相关的错误。 理想情况下

  2. 那些喜欢解决谜题的人可以看到扩展 Thread 的另一个副作用。 下面的代码
    当没有人通知他们时,将打印无法访问的代码。

请参阅http://pastebin.com/BjKNNs2G

public class WaitPuzzle {

    public static void main(String[] args) throws InterruptedException {
        DoNothing doNothing = new DoNothing();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        Thread.sleep(100);
        doNothing.start();
        while(true) {
            Thread.sleep(10);
        }
    }


    static class WaitForever extends  Thread {

        private DoNothing doNothing;

        public WaitForever(DoNothing doNothing) {
            this.doNothing =  doNothing;
        }

        @Override
        public void run() {
            synchronized (doNothing) {
                try {
                    doNothing.wait(); // will wait forever here as nobody notifies here
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Unreachable Code");
            }
        }
    }

    static class DoNothing extends Thread {

        @Override
        public void run() {
            System.out.println("Do Nothing ");
        }
    } 
}

Adding my two cents here -
Always whenever possible use implements Runnable . Below are two caveats on why you should not use
extends Threads

  1. Ideally you should never extend the Thread class; the Thread class should be made final.
    At least its methods like thread.getId().
    See this discussion for a bug related to extending Threads.

  2. Those who like to solve puzzles can see another side effect of extending Thread. The below code
    will print unreachable code when nobody is notifying them.

Please see http://pastebin.com/BjKNNs2G.

public class WaitPuzzle {

    public static void main(String[] args) throws InterruptedException {
        DoNothing doNothing = new DoNothing();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        Thread.sleep(100);
        doNothing.start();
        while(true) {
            Thread.sleep(10);
        }
    }


    static class WaitForever extends  Thread {

        private DoNothing doNothing;

        public WaitForever(DoNothing doNothing) {
            this.doNothing =  doNothing;
        }

        @Override
        public void run() {
            synchronized (doNothing) {
                try {
                    doNothing.wait(); // will wait forever here as nobody notifies here
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Unreachable Code");
            }
        }
    }

    static class DoNothing extends Thread {

        @Override
        public void run() {
            System.out.println("Do Nothing ");
        }
    } 
}
不语却知心 2024-07-20 17:44:26

实现 Runnable 和扩展 Thread 之间的一个区别是,通过扩展 Thread,每个线程都有一个与其关联的唯一对象,而实现 Runnable 时,许多线程可以共享同一个对象实例。

实现 Runnable 的类不是线程,而只是一个类。 对于要由线程执行的 Runnable,您需要创建 Thread 的实例并将 Runnable 实例作为目标传递。

在大多数情况下,如果您只计划重写 run() 方法而不重写其他 Thread 方法,则应使用 Runnable 接口。 这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应对类进行子类化。

当需要扩展超类时,实现 Runnable 接口比使用 Thread 类更合适。 因为我们可以在实现Runnable接口的同时扩展另一个类来创建一个线程。 但如果我们只是扩展 Thread 类,我们就不能从任何其他类继承。

One difference between implementing Runnable and extending Thread is that by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.

A class that implements Runnable is not a thread and just a class. For a Runnable to be executed by a Thread, you need to create an instance of Thread and pass the Runnable instance in as the target.

In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.

When there is a need to extend a superclass, implementing the Runnable interface is more appropriate than using the Thread class. Because we can extend another class while implementing Runnable interface to make a thread. But if we just extend the Thread class we can't inherit from any other class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文