本文共 2669 字,大约阅读时间需要 8 分钟。
synchronized
的含义:
Monitor
), 该Monitor由一个锁(lock), 一个等待队列(waiting queue ), 一个入口队列( entry queue).synchronized
关键字, 该方法可以被任意数量的线程,在任意时刻调用。synchronized
关键字的方法,任意时刻只能被唯一的一个获得了对象实例锁的线程调用。synchronized
用于实现多线程的同步操作wait()
功用
wait()
, notify()
, notifyAll()
和 synchonized
需要搭配使用, 用于线程同步notifyAll()
时才返回。synchronized
的方法内部,调用了wait()
后, 该线程会释放该对象的锁, 然后该线程会被添加到该对象的等待队列中(waiting queue), 只要该线程在等待队列中, 就会一直处于闲置状态, 不会被调度执行。 要注意wait()
方法会强迫线程先进行释放锁操作,所以在调用wait()
时, 该线程必须已经获得锁,否则会抛出异常。由于wait()
在synchonized的方法内部被执行, 锁一定已经获得, 就不会抛出异常了。 notify()
的功用
wait()
, notify()
, notifyAll()
和 synchonized
需要搭配使用, 用于线程同步notify()
方法时, 调度器会从所有处于该对象等待队列(waiting queue)的线程中取出任意一个线程, 将其添加到入口队列( entry queue) 中. 然后在入口队列中的多个线程就会竞争对象的锁, 得到锁的线程就可以继续执行。 如果等待队列中(waiting queue)没有线程, notify()
方法不会产生任何作用notifyAll()
和notify()
工作机制一样, 区别在于notifyAll()
会将等待队列(waiting queue)中所有的线程都添加到入口队列中(entry queue)notifyAll()
比notify()
更加常用, 因为notify()
方法只会唤起一个线程, 且无法指定唤醒哪一个线程,所以只有在多个执行相同任务的线程在并发运行时, 我们不关心哪一个线程被唤醒时,才会使用notify()
wait()
和notify()
或notifyAll()
需要搭配synchronized
关键字使用wait()
之后, 必然需要由另外一个线程调用notify()
来唤醒该线程, 所以本质上, wait()
与notify()
的成对使用, 是一种线程间的通信手段。wait()
操作的调用必然是在等待某种条件的成立, 而条件的成立必然是由其他的线程来完成的。 所以实际上, 我们调用 wait() 的时候, 实际上希望达到如下的效果// 线程A 的代码while(!condition){ // 不能使用 if , 因为存在一些特殊情况, 使得线程没有收到 notify 时也能退出等待状态 wait();}// do something
// 线程 B 的代码if(!condition){ // do something ... condition = true; notify();}
现在考虑, 如果wait()
和 notify()
的操作没有相应的同步机制, 则会发生如下情况(Lost Wake-Up
)
!condition
判断条件, 但尚未执行 wait
方法), CPU 时间片耗尽, CPU 开始执行线程B的代码condition = true; notify();
的操作, 此时【线程A】的 wait()
操作尚未被执行, notify()
操作没有产生任何效果wait()
操作, 进入等待状态,如果没有额外的 notify() 操作, 该线程将持续在 condition = true
的情形下, 持续处于等待状态得不到执行。由此看出, 在使用 wait() 和 notify() 这种会挂起线程的操作时, 我们需要一种同步机制保证, condition
的检查与 wait()
操作, 以及 condition
的更新与 notify()
是互斥的。
// 线程A 的代码synchronized(obj_A){ while(!condition){ wait(); } // do something }
// 线程 B 的代码synchronized(obj_A){ if(!condition){ // do something ... condition = true; notify(); }}
乍一看, 上述的代码可以解决问题, 但是仔细分析一下, 由于wait()
操作会挂起当前线程, 那么必然需要在挂起前释放掉 obj_A
的锁, 但如果 obj_A
允许是任意对象, wait()
函数作为一个没有参数输入的方法,无从得知应该释放哪个对象的锁 。于是很自然的, 语法就会被设计成 java 现在的样子。即基于对象的 wait()
与 notify()
的调用, 必须先获得该对象的锁。
正确的用法示例如下
// 线程 A 的代码synchronized(obj_A){ while(!condition){ obj_A.wait(); } // do something }
// 线程 B 的代码synchronized(obj_A){ if(!condition){ // do something ... condition = true; obj_A.notify(); }}