Java线程:等待并通知方法

我有一个调用wait方法的线程,只能在从其他类调用notify方法时唤醒:

  class ThreadA { public static void main(String [] args) { ThreadB b = new ThreadB(); b.start(); synchronized(b) { try { System.out.println("Waiting for b to complete..."); b.wait(); } catch (InterruptedException e) {} System.out.println("Total is: " + b.total); } } } class ThreadB extends Thread { int total; public void run() { synchronized(this) { for(int i=0;i<100;i++) { total += i; } notify(); } } } 

在上面的代码中,如果在mainsynchronized块,如果ThreadA没有先执行而是另一个同步块执行并完成完成,则ThreadA执行其synchronized块并调用wait ,将会发生什么以及它将如何再次通知?

如果ThreadBThreadA执行之前通过其synchronized块,则ThreadA将无限期地阻塞wait调用。 不会以某种方式通知其他线程已经完成。

问题在于您尝试使用waitnotify ,而不是设计使用它们。 通常, waitnotify用于让一个线程等待直到某个条件为真,然后让另一个线程发出信号表明该条件可能已经成立。 例如,它们通常如下使用:

 /* Producer */ synchronized (obj) { /* Make resource available. */ obj.notify(); } /* Consumer */ synchronized (obj) { while (/* resource not available */) obj.wait(); /* Consume the resource. */ } 

上面的代码工作的原因是首先运行哪个线程并不重要。 如果生产者线程创建了一个资源,并且没有人在wait obj ,那么当消费者运行时,它将进入while循环,注意资源已经生成,然后跳过调用wait 。 然后它可以消耗资源。 另一方面,如果消费者首先运行,它将在while循环中注意到该资源尚未可用并将wait某个其他对象通知它。 然后,另一个线程可以运行,生成资源,并notify消费者线程资源可用。 一旦原始线程被唤醒,它将注意到循环的条件不再是真的并且将消耗该资源。

更一般地说,Java建议你总是在循环中调用wait ,因为虚假的通知中线程可以从wait的调用中唤醒,而不会得到任何通知。 使用上述模式可以防止这种情况。

在您的特定实例中,如果您想确保在ThreadA执行之前ThreadB已经完成运行,您可能希望使用Thread.join() ,它会显式阻塞调用线程,直到其他线程执行。 更一般地说,您可能希望查看Java提供的一些其他同步原语,因为它们通常比waitnotify更容易使用。

你可以循环并等到计算总数:

 synchronized(b) { while (total == 0) { b.wait(); } } 

您还可以使用更高级别的抽象,如CountDownLatch 。

在ThreadA.main中输入synchronized块之前,可以完成ThreadB的run方法。 在这种情况下,由于在您开始等待之前发生了通知呼叫,因此ThreadA将在等待呼叫时永久阻止。

一个简单的解决方法是在启动第二个线程之前获取main中的b锁,以确保首先发生等待。

 ThreadB b = new ThreadB(); synchronized(b) { b.start(); ... b.wait(); } 

您可能想要使用java.util.concurrent.Semaphore。

1)您需要添加一些用于线程之间通信的标志,以便B在完成时可以向A发送信号。 一个简单的布尔变量很好,只要它只在同步块中读写。

 synchronized(this) { for(int i=0;i<100;i++) { total += i; } isDone = true; notify(); } 

2)等待时需要循环。 因此,如果你的布尔变量被称为isDone,并且被threadB设置为true,那么threadA应该有这样的代码:

 synchronized(b) { System.out.println("Waiting for b to complete..."); while( ! isDone ) b.wait(); } 

在这种特殊情况下,实际上没有理由在A中使用synchronized块 - 因为threadB在完成运行后没有做任何事情,A除了等待B之外什么都不做,threadA可以简单地调用b.join()阻止直到它完成。 我假设你的实际用例比这更复杂。

为什么要复杂化? 只需使用Thread的join()函数。

 ThreadB b = new ThreadB(); b.start(); b.join(); // now print b.total 

synchronized(thread) ,不要做,不synchronized(thread) ..重新:没有synchronized(thread) 🙂

如果你需要等待线程’b’完成,使用b.join(),现在你的代码可以在b.wait()中自由挂起

希望下面的源代码可以在同步(线程)/ notify()时给你一个洞察力我认为是不好的做法。 (切切)

请享用


要进行下面的操作,请确保您已接受Oracle的许可协议,请访问: https ://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewLicense- 开始?LicenseUUID = 7HeJ_hCwhb4AAAEtmC8ADqmR&ProductUUID = pGqJ_hCwj_AAAAEtB8oADqmS&CNUM = evsref = SLN =

Java源代码(incl),在init()中调用,从java 1.5开始,由任何java c-tor实际调用

 private static **synchronized int** nextThreadNum() { return threadInitNumber++; } 

// join(如果nanos> 500000,millis == 0和nanos> 0,那么w / nanos的方法只会将millis增加1

 public final **synchronized** void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } public **synchronized** void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } } 

//在stop之后调用stop1确保适当的权限

 private final **synchronized** void stop1(Throwable th) { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if ((this != Thread.currentThread()) || (!(th instanceof ThreadDeath))) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW" if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise stop0(th); } else { // Must do the null arg check that the VM would do with stop0 if (th == null) { throw new NullPointerException(); } // Remember this stop attempt for if/when start is used stopBeforeStart = true; throwableFromStop = th; } }