在阅读here中Thread和Runnable之间的重大差异时,我遇到了一个不同之处:
扩展Thread类时,每个线程都会创建唯一的对象并与之关联。其中
实现Runnable时,它会将同一个对象共享给多个线程。。
有代码给出:
class ImplementsRunnable implements Runnable {
private int counter = 0;
public void run() {
counter++;
System.out.println("ImplementsRunnable : Counter : " + counter);
}
}
class ExtendsThread extends Thread {
private int counter = 0;
public void run() {
counter++;
System.out.println("ExtendsThread : Counter : " + counter);
}
}
public class ThreadVsRunnable {
public static void main(String args[]) throws Exception {
//Multiple threads share the same object.
ImplementsRunnable rc = new ImplementsRunnable();
Thread t1 = new Thread(rc);
t1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t2 = new Thread(rc);
t2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t3 = new Thread(rc);
t3.start();
//Creating new instance for every thread access.
ExtendsThread tc1 = new ExtendsThread();
tc1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc2 = new ExtendsThread();
tc2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc3 = new ExtendsThread();
tc3.start();
}
}
输出是这样的:
ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
它证明了上面给出的差异。我在下面的代码中做了一些修改:
class ImplementsRunnable implements Runnable {
private int counter = 0;
public void run() {
counter++;
System.out.println("ImplementsRunnable : Counter : " + counter);
}
}
class ExtendsThread extends Thread {
private int counter = 0;
public void run() {
counter++;
System.out.println("ExtendsThread : Counter : " + counter);
}
}
public class ThreadVsRunnable {
public static void main(String args[]) throws Exception {
//Multiple threads share the same object.
ImplementsRunnable rc = new ImplementsRunnable();
Thread t1 = new Thread(rc);
t1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t2 = new Thread(rc);
t2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t3 = new Thread(rc);
t3.start();
//Modification done here. Only one object is shered by multiple threads here also.
ExtendsThread extendsThread = new ExtendsThread();
Thread thread11 = new Thread(extendsThread);
thread11.start();
Thread.sleep(1000);
Thread thread12 = new Thread(extendsThread);
thread12.start();
Thread.sleep(1000);
Thread thread13 = new Thread(extendsThread);
thread13.start();
Thread.sleep(1000);
}
}
现在输出是:
ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 2
ExtendsThread : Counter : 3
我理解这一事实,这里同一个对象(extendsThread)由三个线程共享。但我在这里很困惑,它与实现Runnable有何不同。在这里,即使* ExtendsThread * 扩展了Thread,我们仍然可以将此类的对象共享给其他线程。在我看来,上述差异没有任何意义。
感谢。
答案 0 :(得分:9)
以下是javadoc州
的内容有两种方法可以创建新的执行线程。一种方法是 声明一个类是Thread的子类。这个子类应该 覆盖Thread类的run方法。子类的一个实例 然后可以分配和启动。例如,计算的线程 大于规定值的素数可写成如下:
创建线程的另一种方法是声明一个实现的类 Runnable接口。该类然后实现run方法。一个 然后可以分配类的实例,作为参数传递 在创建Thread时,并启动。在另一个中使用相同的示例 样式如下所示:
所以两种方式
public class MyThread extends Thread {
// overriden from Runnable, which Thread implements
public void run() {
...
}
}
...
MyThread thread = new MyThread();
thread.start();
或
public class MyRunnable implements Runnable{
public void run() {
...
}
}
...
Thread thread = new Thread(new MyRunnable());
thread.start();
您的counter
字段是一个实例字段。
在第一种情况下,每个在此处创建的对象
ExtendsThread tc1 = new ExtendsThread();
tc1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc2 = new ExtendsThread();
tc2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc3 = new ExtendsThread();
tc3.start();
将拥有自己的副本(这就是实例变量的工作方式)。因此,当您启动每个线程时,每个线程都会增加其自己的字段副本。
在第二种情况下,您使用Thread
子类作为Runnable
构造函数的Thread
参数。
ExtendsThread extendsThread = new ExtendsThread();
Thread thread11 = new Thread(extendsThread);
thread11.start();
Thread.sleep(1000);
Thread thread12 = new Thread(extendsThread);
thread12.start();
Thread.sleep(1000);
Thread thread13 = new Thread(extendsThread);
thread13.start();
Thread.sleep(1000);
它与您传递的ExtendsThread
对象相同,因此其counter
字段会被所有线程递增。它几乎等同于您之前使用的ImplementsRunnable
。
从评论中添加:
要理解的第一件事是Thread
类实现了Runnable
,因此您可以在任何可以使用Thread
的地方使用Runnable
实例。例如,
new Thread(new Thread()); // won't do anything, but just to demonstrate
使用
创建Thread
时
new Thread(someRunnable);
然后启动它,线程调用给定的Runnable
实例的run()
方法。如果Runnable
实例恰好也是Thread
的实例,那就这样吧。这不会改变任何事情。
创建像
这样的自定义线程时new ExtendsThread();
然后启动它,它自己调用run()
。
答案 1 :(得分:3)
实现Runnable的主要区别在于您不“消耗”单个继承。考虑这些类声明:
public class HelloRunnable implements Runnable extends AbstractHello
public class HelloRunnable extends Thread
在继承方面,你可以使用Runnable做更多的事情。
答案 2 :(得分:1)
@BalwantChauhan:Runnable接口的一个常见用法是,我们知道在Java的情况下不可能进行多重继承。现在假设您有一个场景,您希望扩展一个类,并且您还想实现线程。因此,对于那些情况,如果我们继续使用Thread,则无法实现它。 例如:假设(在Java Swing的情况下),如果你想创建一个框架,并且在那个框架类中你想要实现线程,那么就不可能扩展JFrame和Thread类,所以在这种情况下我们扩展了JFrame和实现Runnable。
public class HelloFrame extends JFrame implements Runnable{
...
public void run(){
// thread code
}
...
}