Java-Thread Vs Runnable

时间:2014-02-19 04:30:25

标签: java multithreading

在阅读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,我们仍然可以将此类的对象共享给其他线程。在我看来,上述差异没有任何意义。

感谢。

3 个答案:

答案 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
  }

  ...
}