【问题标题】:Java notify() run before wait()?Java notify() 在 wait() 之前运行?
【发布时间】:2013-11-01 11:45:51
【问题描述】:
public 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){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
}

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            notify();
        }
    }
}

如上例,如果wait()块先进入,ThreadB中后续的notify()会告诉主线程继续。

但是我们不能保证wait()会在notify(之前执行),如果ThreadB先进入block呢? Notify() 将在wait() 之前执行,所以wait() 将永远挂在那里(因为不再有notify() 告诉它继续)?处理这个问题的正确方法通常是什么?

【问题讨论】:

  • 您的示例中没有等待或通知...
  • 有声音不鼓励使用等待/通知并建议使用例如CyclicBarrierCountdownLatch
  • 如果有的话。 (?),您可以使用 java.util.concurrent.Semaphore。将'permits'初始化为0,在信号线程中获取(),在信号器线程中释放()。信号量保留计数,因此信号是否在等待之前出现并不重要 - 它将被存储。
  • 事实上,有多种可能的解决方案可以满足您的要求。
  • 对不起大家!我输入了错误的代码!现在改变了!

标签: java multithreading synchronized


【解决方案1】:

您应该几乎总是将谓词与等待/通知一起使用。也就是说,你需要一个条件 您可以检查,例如变量变为真,队列变为空/满等。只是盲目地等待某人调用 .notify() 用例很少。

所以,以下是不行的,因为你说的原因,其他线程可以在 ThreadA 调用 .wait() 之前调用 .notify()

public class ThreadA {
    public static Object latch = new Object();
    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        b.start();
        synchronized(latch ) {
            latch.wait(); //wait for B to finish a calculation
        }
        System.out.println("Total is: " + b.total);

    }
}

class ThreadB extends Thread {
    int total;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            total += i;
        }
       synchronized(ThreadA.latch) {
           ThreadA.latch.notify();
       }
    }
}

你需要做这样的事情:

 public class ThreadA {
    public static Object latch = new Object();
    public static boolean done = false;
    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        b.start();
        synchronized(latch ) {
            while (!done) {   //wait for B to indicate it is finished.
                latch.wait(); 
            }
        }
        System.out.println("Total is: " + b.total);

    }
}

class ThreadB extends Thread {
    int total;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            total += i;
        }
       synchronized(ThreadA.latch) {
           ThreadA.done = true;         
           ThreadA.latch.notify();
       }
    }
}

请注意,在上面,done 变量受同步块保护,.wait() 将自动释放/重新获取该锁。所以没有竞争条件,如果 .notify() 在我们到达 .wait() 调用之前被调用,ThreadA 会发现因为 done 将是 true 并且根本不进入 .wait() 调用。

对于这个代码这样简单的情况,你可以等待线程B结束,可以用b.join();完成

【讨论】:

  • 简单地说:while(true){synchronized(lock){while(!condition){lock.wait()}}}
【解决方案2】:

您的问题的许多可能解决方案之一是:

public class ThreadA {
  public static final CyclicBarrier barrier = new CyclicBarrier(2);

  public static void main(String[] args) {
    ThreadB b = new ThreadB();
    b.start();
    try {
      barrier.await();
      System.out.println("Total is: " + b.total);
    } catch (InterruptedException | BrokenBarrierException ex) {
    }
  }
}

class ThreadB extends Thread {
    int total;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            total += i;
        }
        try {
          ThreadA.barrier.await();
        } catch (InterruptedException | BrokenBarrierException ex) {
        }
    }
}

【讨论】:

    【解决方案3】:

    我猜你想做这样的事情

    public class ThreadA {
        public static void main(String[] args) {
            ThreadB b = new ThreadB();
            b.start();
            b.join(); // Wait for thread b to finish 
    
            System.out.println("Total is: " + b.total);
    
        }
    }
    

    您还应该让 ThreadB 只实现 Runnable 而不是扩展 Thread

    class ThreadB implements Runnable {
        int total;
    
        public void run() {
            for (int i = 0; i < 100; i++) {
                total += i;
            } 
        }
    }
    

    然后使用它

    ThreadB tb = new ThreadB();
    Thread b = new Thread(tb);
    

    【讨论】:

    • 问题是wait()应该在notify()之后执行,在你的代码中wait()总是最后执行。
    • @Aslamanwer OP 要求的问题是,当 waitnotify 之后被调用时,ThreadA 将永远等待,因为 ThreadB 已经消失,因此永远不会调用 @ 987654330@ 再次在同步监视器上。尽管如此,OP 试图解决的问题是 ThreadA 应该一直等到 ThreadB 完成。这就是join 的意义所在,因为它会执行一个循环并检查线程是否还活着。如果是这样,它会等待。所以我不明白你的评论。
    • 嗨 Rene,你是对的,我没有看到你的完整代码。
    猜你喜欢
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    相关资源
    最近更新 更多