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状态的所有线程