Java线程:等待并通知方法

时间:2011-02-25 18:34:02

标签: java multithreading wait notify

我有一个调用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,将会发生什么以及如何再次通知?

7 个答案:

答案 0 :(得分:10)

如果ThreadBsynchronized之前ThreadA阻止了ThreadA阻止,那么wait将无限期阻止对wait的呼叫。不会以某种方式通知其他线程已经完成。

问题在于您尝试使用notifywait,而不是设计使用它们。通常,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)

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

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

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

答案 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;
    }
}