首先明确每个对象的对象头中有Marked Word字段,其中正常是记录一些hashcode、分代年龄等信息。接着了解每个对象会关联一个 Monitor对象(ObjectMonitor),也就是所谓的监视器/管程,依赖于底层的操作系统的 Mutex Lock 来实现,因此每次加锁都涉及到用户态到内核态的切换。其中有三个部分,一个为 owner set 用于存储持有该监视器的线程,一个为entry set 用于存储正在等待获取监视器的线程,一个为wait set 用于存储被阻塞的线程【因此wait才只能在synchronized同步代码块中使用】。
当一个对象被加在synchronized的括号中【被加上锁】的时候,相当于在对象的Marked Word 字段中存储一个指向Monitor对象的指针,后续每当有线程对象进来时,就会找到Monitor对象,先查看owner set中有无线程对象,如果没有就存入自己,相当于持有了锁;如果有则会进入entry set的等待队列中。等到owner set中的线程执行完毕,释放了锁之后,会唤醒处于entry set 的所有线程对象,让他们去竞争锁,这个过程是非公平的,不一定先到先得。
在字节码层面,当进入同步代码块之前会有一个 monitorenter的指令,退出会有一个monitorexit指令,并且其中会有锁释放的保证机制,一旦同步代码块中发生异常,会自动跳转到释放锁对象的代码,不用担心锁永不释放的问题。而synchronized修饰的方法并不会加上这两个指令,而是在方法加上ACC_SYNCHRONIZED标识,表明是一个同步方法。