synchronized
synchronized可以保证方法或代码块运行时,同一时刻只有一个方法可以进入到临界区,还可以保证共享变量的内存可见性
同步代码块使用monitorenter
和monitorexit
来进入和退出同步块,对于同步方法来说,是依靠方法修饰符的ACC_SYNCHRONIZED
完成。
无论采取哪种方式,本质上都是对monitor对象的获取,这个获取过程是排他的,也就是说,同一时刻只能有一个线程获取到由synchronized
所保护对象的监视器
当对象由方法块或者这个对象的同步方法调用时,执行方法的线程必须先获取该对象的监视器才能进入方法块和同步方法,而没有获取到监视器的线程将会被阻塞到同步块和同步方法的入口处,进入阻塞状态
Java对象头和monitor是实现synchronized的基础
Java对象头包括两部分
- Mark Word 标记字段,用于存储对象自身的运行时数据,比如哈希码,GC分代,锁状态标志,偏向线程ID等
- Class Pointer 对象指向它的类元数据的指针,虚拟机通过这个指针来确定对象是哪个类的实例
偏向锁
当一个线程访问同步块并获得锁时,会在对象头(Mark Word)和栈帧的锁记录里存储锁偏向的线程ID,以后线程在进入和退出同步块时,不需要通过CAS来加锁和解锁,只需简单的测试一下对象头的Mark Word
里是否存储着指向当前线程的偏向锁,如果其他线程竞争该锁,则尝试用CAS将对象的偏向锁指向当前线程
轻量级锁
线程在执行同步以前,JVM会在当前线程的栈帧里创建用于存储锁记录的空间,并将对象头的Mark Word
复制到锁记录中,官方称为Displaced Mark Word
,然后线程尝试使用CAS将对象头中的Mark Word
替换为指向锁记录的指针,如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程尝试使用自旋来获取锁
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!