并发源码|synchronized、wait与notify
synchronized的底层原理
跟jvm及monitor有关。
如果用到了synchronized关键字,在底层编译后的jvm指令中,会有monitorenter和monitorexit两个指令
每个对象都是有个关联的monitor对象,即对象的Mark Word。里面有个计数器i,一个线程获取到锁以后i++,并且将锁对象设置为当前线程。释放锁时候i–,若i为0 表示完全释放锁,将monitor的锁对象设为null
这个monitor的锁是支持重入加锁的,什么意思呢,好比下面的代码片段
synchronized(myObject) {
// 一大堆的代码
synchronized(myObject) {
// 一大堆的代码
}
}
如果一个线程第一次synchronized那里,获取到了myObject对象的monitor的锁,计数器加1,然后第二次synchronized那里,会再次获取myObject对象的monitor的锁,这个就是重入加锁了,然后计数器会再次加1,变成2
这个时候,其他的线程在第一次synchronized那里,会发现说myObject对象的monitor锁的计数器是大于0的,意味着被别人加锁了,然后此时线程就会进入block阻塞状态,什么都干不了,就是等着获取锁
接着如果出了synchronized修饰的代码片段的范围,就会有一个monitorexit的指令,在底层。此时获取锁的线程就会对那个对象的monitor的计数器减1,如果有多次重入加锁就会对应多次减1,直到最后,计数器是0
然后后面block住阻塞的线程,会再次尝试获取锁,但是只有一个线程可以获取到锁
block以后,将block的线程加入wait set等待其他线程完成执行
wait与sleep的区别
前者释放锁,后者不释放锁
wait(),必须是有人notify唤醒他
wait(timeout),阻塞一段时间,然后自己唤醒,继续争抢锁
wait与notify,必须在synchronized代码块中使用,因为必须是拥有monitor lock的线程才可以执行wait与notify操作
因此wait与notify,必须与synchornized一起,对同一个对象进行使用,这样他们对应的monitor才是一样的
notify()与notifyall():前者就唤醒block状态的一个线程,后者唤醒block状态的所有线程
- 原文作者:
- 原文链接:https://leyou240.github.io/post/juc02_java%E5%B9%B6%E5%8F%91_synchronizedwait%E4%B8%8Enotify/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。