从我在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

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


当前回答

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

其他回答

这也许不是答案,但无论如何;还有一种创建线程的方法:

Thread t = new Thread() {
    public void run() {
        // Code here
    }
}

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

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

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

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

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

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

一般来说,我建议使用Runnable而不是Thread这样的工具,因为它允许您保持工作与并发选择之间的松散耦合。例如,如果您使用了一个Runnable,并且稍后决定它实际上不需要自己的Thread,那么您可以调用threadA.run()。

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

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

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

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

因此,如果我们用threadA替换它们的runable,我们会得到以下结果:

new FutureTask<Object>(threadA, null)

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

如果您想尝试使用线程池,上面的代码片段将变成如下(使用Executors.newCachedThreadPool()工厂方法):

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

在这里加上我的两分钱-始终尽可能使用机具Runnable。以下是关于为什么不应该使用扩展线程

理想情况下,您不应该扩展Thread类;线程类应为最终类。至少它的方法像thread.getId()。有关扩展线程的错误,请参阅本讨论。那些喜欢解谜的人可以看到扩展线程的另一个副作用。以下代码将在无人通知时打印无法访问的代码。

请参见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 ");
        }
    } 
}