Понимание методов Java Wait и Notify - PullRequest
7 голосов
/ 12 апреля 2010

У меня есть следующая программа:

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, то выполнение программы приостанавливается. Другими словами, я думаю, что это приостанавливает текущий поток выполнения и, в свою очередь, исполнителя. Буду признателен за любую помощь в понимании происходящего.

Это область1, где теория и javadoc кажутся простыми, и поскольку примеров не так много, концептуально оставил во мне большой пробел.

Ответы [ 3 ]

12 голосов
/ 12 апреля 2010

Вы звоните wait и notifyAll на obj, но вы синхронизируете на this (потому что у вас есть синхронизированные методы).

Чтобы подождать или уведомить, сначала нужно «владеть» монитором. Отмените синхронизацию методов и вместо этого выполните синхронизацию по объекту:

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");
}
3 голосов
/ 12 апреля 2010

Либо synchronize на obj, либо звоните wait и notify на this. Вызывающий поток должен содержать монитор того же объекта , для которого вызываются эти методы.

Например,

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();
  }

}
1 голос
/ 04 марта 2013

Вместо того, чтобы поставить прямое утверждение pool.shutdown(), попробуйте, как показано ниже.

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

Таким образом, он будет ждать завершения всех потоков.

...