java同步机制之synchronized与lock-part1

Posted by My Blog on June 2, 2021

[toc]

0.从问题出发

1 synchronized vs lock,既生瑜,何生亮?
2 synchronized是如何实现的?
3 所谓对象与monitor关联,是什么意思?
4 synchronized对象时,对象头会有变化。ReentrantLock加锁后,对象头本身变化吗?
5 object的wait()/notify()方法有什么用?
6 hotspot内部是如何实现的?

1.概述

本文意在探讨java两大类同步机制synchronized与锁(基于AbstractQueuedSynchronizer如ReentrantLock)的异同,及其背后的实现原理。首先给出一些结论,后续验证和分析:

  • 前者内置于java语言本身,是语法糖,重在开发便捷性和效率;后者由jdk实现,重在灵活性和性能
  • synchronized基于monitor概念设计,在jvm内部实现
  • ReentrantLock等锁基于AQS(AbstractQueuedSynchronizer),在jdk中实现
  • 偏向/轻量/重量锁等概念是jdk1.6针对synchronized的性能优化,与其它锁机制无关,对其它锁而言,对象头不会变化
  • synchronized通过Object的wait/notify方法提供隐性条件协作

2.基础概念

2.1 java对象头

任意java对象都包含对象头,以保存相关描述信息

对象头 Mark word
header mark word

2.2 monitor

monitor是由C.A.R. Hoare等人提出的一种线程同步机制,提供:

  • 互斥功能
  • 线程间协作

ref: 1 2

3.对象头在加锁时的变化

3.1 synchronized(偏向/轻量/重量锁)

上述三种锁是jdk1.6为了提升synchronized性能,针对不同场景进行的三种内部优化,这三种实现会修改markword:tag字段。通过jol包打印header取值情况:

1
2
3
4
5
6
7
8
Thread.sleep(5000);
    Object o = new Object();
    System.out.println("未进入同步块,MarkWord 为:");
    System.out.println(ClassLayout.parseInstance(o).toPrintable());
    synchronized (o){
    	System.out.println(("进入同步块,MarkWord 为:"));
    	System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
object header同步前后变化
object header output when synchronized

至于上述三种内部锁适用于何种场景及其流转,不是本文重点,详见难搞的偏向锁终于被 Java 移除了

3.2 Lock加锁时锁对象头会变化吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
ReentrantLock rl = new ReentrantLock();
System.out.println("new ReentrantLock: ");
System.out.println(ClassLayout.parseInstance(rl).toPrintable());
rl.lock();
System.out.println("    after locking: ");
System.out.println(ClassLayout.parseInstance(rl).toPrintable());
try {
   System.out.println("    critical running:");
} finally {
   rl.unlock();
   System.out.println("    release locking: ");
   System.out.println(ClassLayout.parseInstance(rl).toPrintable());
}
加锁前后object header无变化
lock object header

4.synchronized的实现

上述代码编译后字节码:

synchronized在编译后, 在jvm内部由指令monitorenter/monitorexit实现 bytecode
the java virtual machine specification 8中的定义 monitorenter

根据描述,当锁对象关联的monitor的entry count:

  • 0 时,取得锁,成为持有者
  • 不为0当已经持有时,则增1,重入持有
  • 不为0且被其它线程持有,则阻塞

5.所谓每个Object关联一个monitor意味着什么?

根据Monitors and Exceptions : How to Implement Java efficiently: Andreas Krall and Mark Probst

sun的实现是使用锁对象的object identifier在hashmap中关联monitor对象(首次加锁时生成)。当然,实际上各个版本的jvm到底如何实现,还需要具体研究jvm源码,本文暂时止步于此34。有个monitor参考实现5

6.synchronized时的线程间协作

通过Object对象中的wait()/notify()方法,二者包含且仅包含了一个隐性Condition变量

7.synchronized锁模型示意

synchronized实现逻辑模型
synchronized locking model

8.references