理解Java的Wait和Notify方法

7

我有一个以下的程序:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class SimpleWaitNotify implements Runnable {

final static Object obj = new Object();
static boolean value = true;

public synchronized void flag()  {
    System.out.println("Before Wait");
    try {
        obj.wait();
    } catch (InterruptedException e) {
        System.out.println("Thread interrupted");
    }
    System.out.println("After Being Notified");
}

public synchronized void unflag() {
    System.out.println("Before Notify All");
    obj.notifyAll();
    System.out.println("After Notify All Method Call");
}

public void run() {
    if (value) {
        flag();
    } else {
        unflag();
    }
}

public static void main(String[] args) throws InterruptedException {
    ExecutorService pool = Executors.newFixedThreadPool(4);
    SimpleWaitNotify sWait = new SimpleWaitNotify();
    pool.execute(sWait);
    SimpleWaitNotify.value = false;
    SimpleWaitNotify sNotify = new SimpleWaitNotify();
    pool.execute(sNotify);
    pool.shutdown();

}

当我等待obj时,两个线程中的每一个都会抛出以下异常Exception in thread "pool-1-thread-1" java.lang.IllegalMonitorStateException: current thread not owner

但是如果我使用SimpleWaitNotify的监视器,程序执行将被暂停。换句话说,我认为它会暂停当前执行线程,从而暂停执行者。任何有助于理解正在发生的事情的帮助都将不胜感激。

这是一个理论和javadoc似乎很简单的领域,由于没有太多的例子,在概念上留下了一个很大的空白。

3个回答

12
你正在调用obj对象的wait()和notifyAll()方法,但你又在使用同步方法,所以锁定了this对象。
为了进行等待或通知,你需要先拥有这个监视器。 取消同步方法,改为使用obj对象进行同步:
public void flag()  {
    System.out.println("Before Wait");
    synchronized (obj) {
        try {
            obj.wait();
        } catch (InterruptedException e) {
            System.out.println("Thread interrupted");
        }
    }
    System.out.println("After Being Notified");
}

public void unflag() {
    System.out.println("Before Notify All");
    synchronized (obj) {
        obj.notifyAll();
    }
    System.out.println("After Notify All Method Call");
}

好的。不使用复合对象“obj”能否实现相同的功能? - Maddy
4
@Maddy: 你应该能够在“this”上等待并通知,但这通常是一种不好的做法,因为“this”通常是其他代码可以访问的引用。保持对只有你的代码知道的对象的私有引用是良好的实践。 - Jon Skeet

3
要么在obj上同步,要么在this上调用waitnotify。 调用线程必须持有调用这些方法的相同对象的监视器。
例如,
synchronized void flag() {
  System.out.println("Before Wait");
  try {
    wait();
  } catch (InterruptedException e) {
    System.out.println("Thread interrupted");
  }
  System.out.println("After Being Notified");
}

在这个例子中,锁被放在this上(当实例方法使用修饰符synchronized时,会获取实例的监视器)。因此,wait()方法可以在隐含实例this上调用。
为了协调两个线程,它们需要共享相同的锁。原始版本有一个静态的obj,可以用作锁,但它没有在synchronized块中使用。以下是更好的示例:
class SimpleWaitNotify implements Runnable {

  private final Object lock;
  private final boolean wait;

  SimpleWaitNotify(Object lock, boolean wait) {
    this.lock = lock;
    this.wait = wait;
  }

  public void flag()  {
    synchronized (lock) {
      System.out.println("Before Wait");
      try {
        lock.wait();
        System.out.println("After Being Notified");
      } catch (InterruptedException ex) {
        System.out.println("Thread interrupted");
      }
    }
  }

  public void unflag() {
    synchronized(lock) {
      System.out.println("Before Notify All");
      lock.notifyAll();
      System.out.println("After Notify All Method Call");
    }
  }

  public void run() {
    if (wait) {
      flag();
    } else {
      unflag();
    }
  }

  public static void main(String[] argv) throws Exception {
    ExecutorService pool = Executors.newFixedThreadPool(4);
    Object shared = new Object();
    SimpleWaitNotify sWait = new SimpleWaitNotify(shared, true);
    pool.execute(sWait);
    SimpleWaitNotify sNotify = new SimpleWaitNotify(shared, false);
    pool.execute(sNotify);
    pool.shutdown();
  }

}

我改变了等待SimpleWait对象本身。然而,在这种情况下,程序不会停止。相反,挂起的线程没有被通知。我打印出以下内容。在等待之前 在通知所有之前 在调用Notify All方法之后 - Maddy

1

不要直接使用语句pool.shutdown(),请尝试以下方法。

while (!service.isTerminated())
{
    service.shutdown();
}

所以它将等待所有线程执行完成。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接