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

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


当前回答

线程保持不打算访问的行为;

它的同步锁用于连接等。它有一些你可以偶然访问的方法。

但是,如果您的子类Thread必须考虑实现更多Thread。

public class ThreadMain {
    public int getId() {
        return 12345678;
    }

    public String getName() {
        return "Hello World";
    }

    public String getState() {
        return "testing";
    }

    public void example() {
        new Thread() {
            @Override
            public void run() {
                System.out.println("id: "+getId()+", name: "+getName()+", state: "+getState());
            }
        }.start();
    }

    public static void main(String[] args) {
        new ThreadMain().example();
    }
}

如果你运行这个,你可能会期望

id: 12345678, name: Hello World, state: testing

然而,您并没有调用您认为是的方法,因为您使用的是Thread而不是ThreadMain中的方法,相反,您会看到类似

id: 11, name: Thread-0, state: RUNNABLE

其他回答

我想说还有第三种方法:

public class Something {

    public void justAnotherMethod() { ... }

}

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

也许这受到了我最近大量使用Javascript和Actionscript 3的影响,但是这样你的类就不需要实现一个非常模糊的接口,比如Runnable。

这里的每个人似乎都认为实现Runnable是一条路,我并不真的不同意他们的观点,但在我看来,扩展Thread也是有道理的,事实上,您已经在代码中演示了这一点。

如果您实现Runnable,那么实现Runnable的类对线程名称没有控制权,调用代码可以设置线程名称,如下所示:

new Thread(myRunnable,"WhateverNameiFeelLike");

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

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

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

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

这看起来可能是一件小事,但如果你有一个非常复杂的应用程序,其中有很多线程,并且突然“停止”了(可能是因为死锁,也可能是因为网络协议中的一个不太明显的缺陷,或者其他无休止的原因),然后从Java获取堆栈转储,其中所有线程都被称为“线程-1”、“线程-2”、,“Thread-3”并不总是非常有用(这取决于线程的结构,以及您是否可以仅通过它们的堆栈跟踪来有效地区分哪个线程是哪个线程-如果您使用的是多个线程组,它们都运行相同的代码,则不总是可能的)。

尽管如此,您当然也可以通过创建线程类的扩展来以通用的方式执行上述操作,该扩展将其名称设置为其创建调用的堆栈跟踪,然后将其用于Runnable实现而不是标准的java线程类(见下文),但是除了堆栈跟踪之外用于调试的线程名称(对它可能处理的许多队列或套接字之一的引用,例如,在这种情况下,您可能更希望专门针对这种情况扩展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]

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

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

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

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

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

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

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

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

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

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

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

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

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

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