关于多线程:Java-Thread Vs Runnable

Java-Thread Vs Runnable

本问题已经有最佳答案,请猛点这里访问。

当从这里读取线程和可运行线程之间的显著差异时,我遇到了一个差异,即:

扩展线程类时,每个线程都会创建唯一的对象并与之关联。其中as

实现runnable时,它将同一对象共享给多个线程。

有代码给出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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();
   }
 }

输出如下:

1
2
3
4
5
6
ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1

它证明了上面给出的差异。我对下面给出的代码做了一点修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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);
  }
}

现在输出为:

1
2
3
4
5
6
ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 2
ExtendsThread : Counter : 3

我了解这样一个事实,这里相同的对象(extendthread)由三个线程共享。但这里我很困惑,它与实现runnable有什么不同。在这里,即使*extendthread*扩展线程,我们仍然能够将这个类的对象共享给其他线程。在我看来,上述区别没有任何意义。

谢谢。


这是JavaDoc所说的

There are two ways to create a new thread of execution. One is to
declare a class to be a subclass of Thread. This subclass should
override the run method of class Thread. An instance of the subclass
can then be allocated and started. For example, a thread that computes
primes larger than a stated value could be written as follows:

The other way to create a thread is to declare a class that implements
the Runnable interface. That class then implements the run method. An
instance of the class can then be allocated, passed as an argument
when creating Thread, and started. The same example in this other
style looks like the following:

所以这两种方法

1
2
3
4
5
6
7
8
9
10
public class MyThread extends Thread {
    // overriden from Runnable, which Thread implements
    public void run() {
        ...
    }
}

...
MyThread thread = new MyThread();
thread.start();

1
2
3
4
5
6
7
8
public class MyRunnable implements Runnable{
    public void run() {
        ...
    }
}
...
Thread thread = new Thread(new MyRunnable());
thread.start();

您的counter字段是一个实例字段。

在第一种情况下,这里创建的每个对象

1
2
3
4
5
6
7
8
 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构造函数的参数。

1
2
3
4
5
6
7
8
9
10
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,因此您可以在任何可以使用Runnable的地方使用Thread实例。例如,

1
new Thread(new Thread()); // won't do anything, but just to demonstrate

当使用

1
new Thread(someRunnable);

然后启动它,线程调用给定的Runnable实例的run()方法。如果这个Runnable实例恰好也是Thread的实例,那就这样吧。这不会改变任何事情。

当你创建一个自定义线程

1
new ExtendsThread();

一开始,它自己就给run()打电话。


实现runnable的主要区别在于,您不"消耗"单个继承。考虑这些类声明:

1
2
3
public class HelloRunnable implements Runnable extends AbstractHello

public class HelloRunnable extends Thread

当涉及到继承时,您可以用runnable做更多的事情。


@ BalwantChauhan:Runnabl接口的一个常见用法是,我们知道,在Java的情况下,多重继承是不可能的。现在假设您有一个场景,您希望扩展一个类,并且还希望实现线程。所以对于这些场景,如果我们继续执行线程,那么就不可能实现它。例如:假设(在JavaSwing的情况下),如果要创建一个框架,并且在要实现线程的那个框架类中,则不可能扩展JFrand和Trand类,在这种情况下,我们扩展了JFrand并实现了RunnEnabl。

1
2
3
4
5
6
7
8
9
10
public class HelloFrame extends JFrame implements Runnable{

  ...

  public void run(){
    //  thread code
  }

  ...
}