synchronized 实现等待-通知机制
Java中通过关键字synchronized
来对对象加锁,同时配合wait()
,notify()
,notifyAll()
来实现等待-通知机制。但是在实际使用时,容易混淆wait()
和notifyAll()
的含义,下面会详细讨论一下这两个方法的工作原理。
synchronized,wait,notifyAll的工作原理
Java中通过关键字synchronized
来对对象加锁,确保在多线程的环境下,通过synchronized
加锁的对象同一时间只有一个线程可以访问。它的工作原理可以简单概括为:Java中的每一个对象都可以成为一个监视器(Monitor),这个监视器由一个锁(lock),一个等待队列(waiting queue),和一个入口队列(entry queue)组成。
当一个线程取得了synchronized
锁的访问,并在其内部调用wait()
方法后,该线程会释放当前持有的对象的锁,然后该线程会被添加到该对象的等待队列中(waiting queue),并一直处于闲置状态,不会被调用执行。由于调用wait()
方法首先会释放所持有的对象的锁,所以wait()
只能在synchronized
内部(也就是获得对象锁的前提下)执行,否则会抛出异常。
直到其它线程调用notify()
notifyAll()
方法时,调度器会从所有处于该对象等待队列(waiting queue)中的线程中取出任意线程,将其添加进入口队列(entry queue)中,然后入口队列中的线程会竞争对象的锁,得到锁的线程就可以继续执行。
notify()
与notifyAll()
的区别是
notifyAll()
会将等待队列中所有的线程都添加到入口队列中notify()
会将等待队列中任意一个线程都添加到入口队列中
一个例子
下面我们用一个例子来说明synchronized
配合wait()
,notify()
,notifyAll()
的使用方式。
假设有这样一个需求:
给定三个线程,分别命名为A、B、C,要求这三个线程按照顺序交替打印ABC,每个字母打印100次,最终输出结果为:
A
B
C
……
我们通过synchronized``wait()
,notify()
,notifyAll()
来实现,代码如下:
1 | public class PrintABC { |
运行结果
1 | A |
说明:
wait()
方法是放在while
循环里,而不是if
,这是因为存在一些特殊情况,会使得线程没有收到notify
的信号时也能退出等待状态。notify()
方法唤起线程,会先唤起先进入等待队列的线程,而不是随机环形notifyAll()
方法唤起线程,默认会先唤起最后进入等待队列的线程,然后依次唤醒倒数第二个,倒数第三个线程,以此类推,即LIFO策略notify()
,notifyAll()
不会释放对象锁wait/nofity
是通过JVM里的park/unpark
机制来实现的
wait ()方法和 sleep()的区别
- sleep 是线程中的方法,但是 wait 是 Object 中的方法。
- sleep 方法不会释放 lock,但是 wait 会释放,而且会加入到等待队列中。
- sleep 方法不依赖于同步器 synchronized,但是 wait 需要依赖 synchronized 关键字。
- sleep 不需要被唤醒(休眠之后推出阻塞),但是 wait 需要(不指定时间需要被别人中断)。
- 标题: synchronized 实现等待-通知机制
- 作者: zjun
- 创建于 : 2016-09-08 21:16:12
- 更新于 : 2023-12-11 22:03:13
- 链接: https://zjun.site/2016/09/46566b8b6e0d.html
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。