扩展 Thread class 时无法同步两个线程,但在实现 Runnable 接口时可以工作

Unable to synchronize two threads when Thread class is extended but works when implementing Runnable interface

此处同步块不起作用:

class NewThreadt extends Thread {
synchronized void dota(){
      for(int i = 5; i > 0; i--)
      {
      System.out.println(i);
  try { 
        Thread.sleep(500); 

    } catch (InterruptedException e) { 
      System.out.println("Child interrupted."); 
    } 
      }

  }

  // This is the entry point for the second thread. 
  public void run() { 
    dota() ;
    System.out.println("Exiting child thread."); 
    System.out.println(Thread.currentThread());    //gives the name of thread 

  } 
} 

class trial { 
  public static void main(String args[]) { 
    NewThreadt t=new NewThreadt();
    NewThreadt q=new NewThreadt();
    t.start();
    q.start();   both are in main


    System.out.println("Main thread exiting."); 
  } 
}

输出:

Main thread exiting.
5
5
4
4
3
3
2
2
1
1

但是当我进行以下更改时,同步块起作用:

class NewThreadt implements Runnable  //instead of class NewThreadt extends Thread 

NewThreadt abc=new NewThreadt();  //Changes in main()
Thread t=new Thread(abc);    
Thread q=new Thread(abc);
t.start();
q.start(); 

输出:

Main thread exiting.
5
4
3
2
1    
5
4
3
2
1

为什么会这样?这两个例子不是应该一样工作吗?

同步依赖于共享一个公用锁。实例方法上的 synchronized 关键字意味着要使线程进入该方法,它首先必须获取 this.

上的锁

在你的第一个例子中没有共享锁,两个线程中的每一个都在锁定自己。每个线程获取自己的锁并且没有人阻塞,两个线程 运行 并发。 (这里唯一的锁定是,当一个线程想要写入控制台时,它必须首先获取控制台的锁,以便每个 println 以原子方式发生。)

在第二个示例中,相同的 Runnable 被传递到每个线程,因此它们都锁定在同一个对象上,即 Runnable。一个线程获取锁并执行,另一个必须等​​待第一个线程释放锁。

如果你改变第二个例子给每个线程一个单独的Runnable:

Thread t=new Thread(new NewThreadt());    
Thread q=new Thread(new NewThreadt());
t.start();
q.start(); 

然后您会看到同步不起作用,它的行为与第一个示例相同。