wait和notify这个为什么要在synchronized代码块中?

184 阅读3分钟

当涉及到多线程编程时,notify()wait() 是Java中用于线程间通信的两个重要方法,它们通常与 synchronized 关键字一起使用。

wait()

  • wait() 方法使当前线程进入等待状态,直到其他线程调用相同对象上的 notify()notifyAll() 方法来唤醒该线程。
  • wait() 方法必须在同步块或同步方法中调用,以确保线程安全。
  • 当线程调用 wait() 方法时,它会释放对象的锁,允许其他线程访问同步块或同步方法。
  • 线程在等待状态中不会占用CPU时间,直到它被唤醒。

notify() 和 notifyAll()

  • notify() 方法唤醒等待在相同对象上的一个线程,而 notifyAll() 方法则唤醒所有等待在相同对象上的线程。
  • 被唤醒的线程不会立即执行,而是需要等待调用 notify()notifyAll() 方法的线程释放锁之后才能继续执行。
  • 通常情况下,应该使用 notifyAll() 来确保所有等待线程都有机会被唤醒,以避免因为某些线程处于等待状态而导致死锁。

注意点

notify

This method should only be called by a thread that is the owner of this object's monitor. 只有持有该对象的monitor的线程才能调用notify 方法

wait

The current thread must own this object's monitor.

当前线程必须拥有此对象的monitor。

那么如何获取monitor?

  • By executing a synchronized instance method of that object.
  • By executing the body of a synchronized statement that synchronizes on the object.
  • For objects of type Class, by executing a synchronized static method of that class.

也就是加上 synchronized 对该对象添加monitor

如果没有加上synchronized 会怎么样呢?

java api doc
java 文档描述的是当前线程未持该monitor有会抛出IllegalMonitorStateException

Throws: IllegalMonitorStateException - if the current thread is not the owner of this object's monitor.

下面是示例代码

public class Main {
    public static void main(String[] str){
        Object object = new Object();
        object.notify();
    }
}

最后会抛出异常

Exception in thread "main" java.lang.IllegalMonitorStateException: current thread is not owner
	at java.base/java.lang.Object.notify(Native Method)
	at com.demo.Main.main(Main.java:6)

monitor 归属检查的实现

ObjectMonitor::check_owner 被谁调用呢?

// -----------------------------------------------------------------------------
// Wait/Notify/NotifyAll
//
// Note: a subset of changes to ObjectMonitor::wait()
// will need to be replicated in complete_exit
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
  JavaThread* current = THREAD;

  assert(InitDone, "Unexpectedly not initialized");

  CHECK_OWNER();  //  在这个宏里面被调用


}

CHECK_OWNER 宏展开之后就是调用ObjectMonitor::check_owner

#define CHECK_OWNER()                                                  \
  do {                                                                 \
    if (!check_owner(THREAD)) {                                        \
       assert(HAS_PENDING_EXCEPTION, "expected a pending IMSE here."); \
       return;                                                         \
     }                                                                 \
  } while (false)

ObjectMonitor::check_owner 会抛出异常current thread is not owner

// src/hotspot/share/runtime/objectMonitor.cpp
bool ObjectMonitor::check_owner(TRAPS) {
  JavaThread* current = THREAD;
  void* cur = owner_raw();
  if (cur == current) {
    return true;
  }
  if (current->is_lock_owned((address)cur)) {
    set_owner_from_BasicLock(cur, current);  // Convert from BasicLock* to Thread*.
    _recursions = 0;
    return true;
  }
  THROW_MSG_(vmSymbols::java_lang_IllegalMonitorStateException(),
             "current thread is not owner", false);    // 抛出异常信息current thread is not owner
}

最后我们看一下owner_raw()实现:

// src/hotspot/share/runtime/objectMonitor.inline.hpp
inline void* ObjectMonitor::owner_raw() const {
  return Atomic::load(&_owner);
}

总结

wait和notify这个为什么要在synchronized代码块中?

  • 因为api规定必须持有monitor
  • 因为waitnotify的语义就是为了线程之间协作和通信,为了保证资源的互斥性

相关阅读