Java线程与可运行[重复]

Posted

技术标签:

【中文标题】Java线程与可运行[重复]【英文标题】:Java-Thread Vs Runnable [duplicate] 【发布时间】:2014-03-19 05:28:00 【问题描述】:

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

谢谢。

【问题讨论】:

@helderdarocha 这已作为解决方案提供。 Rupesh Yadav 和 zEro 之间存在冲突。我只是想知道,上述差异有何意义? 混淆来自您的来源;当您实现可运行时,您可以在线程之间共享对象。但你很少会这样做。您也可以使用线程来执行此操作,因为它无论如何都是可运行的,但这会更奇怪。 【参考方案1】:

实现 Runnable 的主要区别在于您不会“消耗”您的单一继承。考虑这些类声明:

public class HelloRunnable implements Runnable extends AbstractHello

public class HelloRunnable extends Thread

在继承方面,您可以使用 Runnable 做更多事情。

【讨论】:

【参考方案2】:

@BalwantChauhan:Runnable 接口的一种常见用法是,我们知道在 Java 的情况下多重继承是不可能的。现在假设您有一个场景,您想要扩展一个类并且还想要实现线程。因此,对于那些场景,如果我们继续使用 Thread,那么就不可能实现它。 例如:假设(在 Java Swing 的情况下),如果你想创建一个框架,并且你想在那个框架类中实现线程,那么就不可能扩展 JFrame 和 Thread 类,所以在这种情况下我们扩展 JFrame 和实现 Runnable。

public class HelloFrame extends JFrame implements Runnable

  ...

  public void run()
    //  thread code
  

  ...
  

【讨论】:

【参考方案3】:

javadoc 声明如下

有两种方法可以创建新的执行线程。一种是 将一个类声明为 Thread 的子类。这个子类应该 重写类 Thread 的 run 方法。子类的一个实例 然后可以分配和启动。例如,计算线程 大于规定值的素数可以写成如下:

创建线程的另一种方法是声明一个实现的类 可运行接口。然后该类实现 run 方法。一个 然后可以分配类的实例,作为参数传递 在创建线程时,并启动。 这个其他的相同示例 样式如下所示:

两种方式

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 构造函数的 Runnable 参数。

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

从 cmets 添加:

首先要了解的是Thread 类实现了Runnable,因此您可以在任何可以使用Runnable 的地方使用Thread 实例。 例如,

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

当你用

创建Thread
new Thread(someRunnable);

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

当你创建一个自定义线程时

new ExtendsThread();

并启动它,它会自行调用run()

【讨论】:

@Sotirious Delimanolis SO,以上区别不相关?? @BalwantChauhan 你能澄清一下你所指的上述差异吗? @Sotirious Delimolis 当然。它是,当您扩展 Thread 类时,您的每个线程都会创建唯一的对象并与之关联。其中,当您实现 Runnable 时,它​​会将同一个对象共享给多个线程.. @BalwantChauhan 首先要了解的是Thread 类实现了Runnable,因此您可以在任何可以使用Runnable 的地方使用Thread 实例。当您使用new Thread(someRunnable) 创建Thread 并启动它时,线程将执行给定的Runnable 实例的run() 方法。如果那个Runnable 实例恰好也是Thread 的一个实例,那么就这样吧,这不会改变任何事情。当您创建像new ExtendsThread() 这样的自定义线程并启动它时,它会自行调用run() @SotiriosDelimanolis 我可以说,通过将线程的引用作为参数传递它只是将旧实例的值复制到新实例吗?因此输出为1,2,3

以上是关于Java线程与可运行[重复]的主要内容,如果未能解决你的问题,请参考以下文章

线程安全与可重入编写方法

线程“main”java.sql.SQLException中的异常:未选择数据库[重复]

java线程只能被启动(Thread.start())一次,那么为啥线程池中的线程能被重复利用呢?

Java——使用多线程从list中不重复地取出数据并进行处理,给多线程任务添加单项任务计时和总耗时

Java——使用多线程从list中不重复地取出数据并进行处理,给多线程任务添加单项任务计时和总耗时

java基础线程池