Java线程:等待和通知方法

Java线程:等待和通知方法,java,multithreading,wait,notify,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("Wait

我有一个线程调用
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();
        }
    }
}
a类{
公共静态void main(字符串[]args){
ThreadB=新的ThreadB();
b、 start();
已同步(b){
试一试{
System.out.println(“等待b完成…”);
b、 等待();
}捕获(中断异常e){}
System.out.println(“总计为:+b.Total”);
}
}
}
类ThreadB扩展线程{
整数合计;
公开募捐{
已同步(此){

对于(int i=0;i您可能希望为此使用java.util.concurrent.Semaphore。

您可以循环并等待,直到计算出总数:

synchronized(b) {
   while (total == 0) {
       b.wait();
   }
}
您还可以使用更高级别的抽象,例如。

不要
同步(线程)
,不要这样做,不要
同步(线程)
。repat:no
同步(线程)
:)

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

--

希望下面的源可以给你一个洞察力,同时同步(线程)/通知()我认为坏的实践。(剪切)

享受


要在下面进行处理,请确保您已接受Oracle的许可协议,请在此处找到:

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);
}
}
1) 您需要添加一些用于线程之间通信的标志,以便B可以在完成时向A发送信号。简单的布尔变量可以,只要它仅在同步块中读取和写入

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

在这种特殊情况下,实际上没有理由在A中使用同步块-因为threadB在完成运行后不会执行任何操作,而A除了等待B之外不会执行任何操作,threadA可以简单地调用B.join()我假设您的实际用例比这更复杂。

如果
ThreadB
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. */
}
上面代码工作的原因是,先运行哪个线程并不重要。如果生产者线程创建了一个资源,而没有人在
obj
上等待
wait
,那么当使用者运行时,它将进入
while
循环,注意资源已经生成,然后跳过对
wait
的调用。然后,它可以使用资源。另一方面,如果使用者首先运行,它将在
while
循环中注意到资源还不可用,并将
等待其他对象通知它。然后,另一个线程可以运行,生成资源,并
通知使用者线程该资源可用一旦原始线程被唤醒,它将注意到循环的条件不再为真,并将消耗资源

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


在您的特定实例中,如果要确保
ThreadB
在执行
ThreadA
之前已完成运行,则可能需要使用
Thread.join()
,它会显式阻止调用线程,直到其他线程执行为止。更一般地说,您可能需要研究Java提供的其他一些同步原语,因为它们通常比
wait
notify

更易于使用。ThreadB的run方法可以在您执行之前完成在这种情况下,由于notify调用发生在您开始等待之前,ThreadA将在等待调用中永久阻塞

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

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

为什么要这么复杂?只需使用线程的join()函数

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

@templatetypedef:感谢您重新格式化(我刚刚开始这么做)。notify()是为神保留的。凡人都应该使用notifyAll()@Julius Davies,ParkSuppot.park/unpark更接近众神,notify仍然非常noobish:)@bestsss解释你自己。你是说park/unpark比wait/notify更有效吗?@Pacerier,虽然它在某一点上进行了调整,现在需要额外的内存围栏。park/unpark不需要显式同步(这很好,虽然它仍然可以使用本机互斥),但正确使用可能需要队列。对于简单的情况,park/unpark通常优于wait/notify,但没有notifyAll(请参阅关于队列的备注)。也不需要将所有内容都包装在try/catch(InterruptedException)中对于为什么不应该在另一个线程对象上同步,这个答案没有提供任何理由,也没有详细回答原始问题。templatetypede、b/c join和join()以及start()使用同步。您不想(通常)弄乱它们。答案是准确的:如果使用同步(线程)/notify()则使用b.join()你把事情搞砸了,有人在等你吗
ThreadB b = new ThreadB();
synchronized(b) {
    b.start();
    ...
    b.wait();
}
ThreadB b = new ThreadB();
b.start();
b.join();
// now print b.total