从我在Java中使用线程的时间来看,我发现了两种编写线程的方法:

使用可运行的机具:

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

或者,使用扩展线程:

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

这两个代码块有什么显著的区别吗?


当前回答

对于大多数工作线程来说,最好的方法是将线程完全封装在工作线程类中,这样就不会有任何外部干扰,导致不需要的和无效的线程/类状态。

我刚刚发布了一个示例,因此我也将与您分享:

/**
 * This worker can only run once
 * @author JayC667
 */
public class ProperThreading {

    private final Thread        mThread         = new Thread(() -> runWorkingLoop());   // if you want worker to be able to run multiple times, move initialisation into startThread()
    private volatile boolean    mThreadStarted  = false;
    private volatile boolean    mStopRequested  = false;

    private final long          mLoopSleepTime;

    public ProperThreading(final long pLoopSleepTime /* pass more arguments here, store in members */ ) {
        mLoopSleepTime = pLoopSleepTime;
    }

    public synchronized void startThread() {
        if (mThreadStarted) throw new IllegalStateException("Worker Thread may only be started once and is already running!");
        mThreadStarted = true;
        mThread.start();
    }

    private void runWorkingLoop() {
        while (!mStopRequested /* && other checks */ ) {
            try {
                // do the magic work here
                Thread.sleep(mLoopSleepTime);

            } catch (final InterruptedException e) {
                break;
            } catch (final Exception e) {
                // do at least some basic handling here, you should NEVER ignore exception unless you know exactly what you're doing, and then it should be commented!
            }
        }
    }

    public synchronized void stopThread() {
        if (!mThreadStarted) throw new IllegalStateException("Worker Thread is not even running yet!");
        mStopRequested = true;
        mThread.interrupt();
    }

}

其他回答

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

通过扩展线程类,派生类不能扩展任何其他基类,因为java只允许单一继承。相反,通过实现可运行接口,类仍然扩展了其他基类。

实现Runnable和扩展Thread之间最显著的区别如下:

通过扩展Thread,派生类本身就是一个线程对象,而实现Runnable接口则将同一对象共享给多个线程。

实际上,将Runnable和Thread相互比较是不明智的。

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

我想说,多线程只有一种方法,有两个步骤。让我表明我的观点。

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

线程:class线程,我可以说是一个非常特殊的类,它能够启动一个新的线程,这实际上通过它的start()方法实现了多线程。

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

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

可以在线程内运行的东西(可运行)。可以启动新线程的东西。

因此,从技术上和理论上来说,这两个都是启动线程所必需的,一个线程将运行,另一个线程会使线程运行(就像机动车的车轮和发动机)。

这就是为什么不能使用MyRunnable启动线程,需要将其传递给thread的实例。

但是,只有使用类thread才能创建和运行线程,因为类thread实现了Runnable,所以我们都知道thread内部也是一个Runnable。

最后,Thread和Runnable是多线程的补充,而不是竞争对手或替代者。

如果你想分开你的关注点,比如,如果你想创建任务,而谁是处理你任务的工作人员对你来说无关紧要,那么使用Runnable并将你的工作定义为任务。就像使用executor服务一样,您以Runnable/callable的形式定义任务,并将它们交给executor,然后由executor负责。

如果您想自己创建一个工作线程并将任务分配给该工作线程,那么您需要对工作线程进行更多的控制,然后使用thread类。

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

编辑:这原本是说“实现一个接口需要更少的资源”,但无论如何都需要创建一个新的线程实例,所以这是错误的。