我有一个调用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();
}
}
}
在上面的代码中,如果synchronized
中的main
块,如果ThreadA
没有先执行,而另一个同步块执行并完成,那么{{1}执行其ThreadA
块并调用synchronized
,将会发生什么以及如何再次通知?
答案 0 :(得分:10)
如果ThreadB
在synchronized
之前ThreadA
阻止了ThreadA
阻止,那么wait
将无限期阻止对wait
的呼叫。不会以某种方式通知其他线程已经完成。
问题在于您尝试使用notify
和wait
,而不是设计使用它们。通常,notify
和/* 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
将通过其他对象通知它。然后,另一个线程可以运行,生成资源,并wait
资源可用的使用者线程。一旦原始线程被唤醒,它将注意到循环的条件不再成立并将消耗资源。
更一般地说,Java建议您始终在循环中调用wait
,因为虚假通知,其中线程可以从调用ThreadB
唤醒而不会通知任何事情。使用上述模式可以防止这种情况。
在您的特定实例中,如果您想确保在ThreadA
执行之前Thread.join()
已完成运行,您可能需要使用wait
,它明确地阻止调用线程直到其他一些线程执行。更一般地说,您可能希望查看Java提供的一些其他同步原语,因为它们通常比notify
和{{1}}更容易使用。
答案 1 :(得分:1)
答案 2 :(得分:1)
在ThreadA.main中输入synchronized块之前,可以完成ThreadB的run方法。在这种情况下,由于在您开始等待之前发生了通知呼叫,因此ThreadA将在等待呼叫时永久阻止。
一个简单的解决方法是在启动第二个线程之前获取main中b的锁定,以确保首先发生等待。
ThreadB b = new ThreadB();
synchronized(b) {
b.start();
...
b.wait();
}
答案 3 :(得分:0)
您可能希望使用java.util.concurrent.Semaphore。
答案 4 :(得分:0)
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中使用同步块 - 因为threadB在完成运行后没有做任何事情,并且A除了等待B之外什么都不做,threadA可以简单地调用b。 join()阻塞直到它完成。我假设你的实际用例比这更复杂。
答案 5 :(得分:0)
为什么要复杂化?只需使用Thread的join()函数。
ThreadB b = new ThreadB();
b.start();
b.join();
// now print b.total
答案 6 :(得分:-1)
不要synchronized(thread)
,不要这样做,不要synchronized(thread)
.. repat:no 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-Start?LicenseUUID=7HeJ_hCwhb4AAAEtmC8ADqmR&ProductUUID=pGqJ_hCwj_AAAAEtB8oADqmS&cnum=&evsref=&sln=
在init()中调用的Java源代码(incl),有效地由任何java c-tor调用,因为java 1.5
private static **synchronized int** nextThreadNum() {
return threadInitNumber++;
}
// join(w / nanos的方法只增加millis,如果nanos> 500000,millis == 0和nanos> 0
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);
}
}
//停止后调用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;
}
}