java并发相关(一)—— 一切从synchronized说起
笔者初次接触锁相关知识,是经典的卖票问题中引入了synchronize。
一、synchronize的几种用法:
讲到synchronize,总归逃不开synchronize的几种基本用法:
- 修饰实例方法
- 修饰代码块(修饰对象、类)
- 修饰静态方法
下面我们就此逐一讲解:
synchronize修饰实例方法
简单来讲,修饰实例方法,其实锁定的是方法所在的实例。
所以对于同一个java实例,如果用synchronize修饰了方法a与方法b,其实他们使用的是同一把锁。
synchronize修饰代码块
单纯修饰代码块,同修饰实例方法;
synchronized(object),修饰对象实例。
synchronized(a.class),同修饰静态方法。
synchronize修饰静态方法
修饰静态方法,我们通常称为类锁,然而实际上,类锁也是作为对象锁实现的,这又涉及到jvm的类加载。
即当jvm加载类文件时,他会加载java.lang.class实例,而实际上类锁就锁在这个实例上。又因为Class的相关数据存储在永久带PermGen(jdk1.8则是metaspace),永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程;
二、synchronize的底层实现(基础):
说道synchronize的实现,必定绕不开对象在jvm中的布局,直接上图:
-
实例数据:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。
-
对齐填充:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐,这点了解即可。
接下来要着重看下java的对象头,这是实现synchronize的基础:
其中Mark Word在默认情况下存储着对象的HashCode、分代年龄、锁标记位等以下是32位JVM的Mark Word默认存储结构,结构如下图:
锁的升级机制我们会在下一篇中进行分析,附上链接如下:https://www.cnblogs.com/fbw-gxy/p/11689431.html
重量级锁会指向一个Monitor的指针,对于Monitor对象,他是由ObjectMonitor实现,HotSpot中由c++实现,这一点我们会在synchronize的可重入性中分析。
参考文章:
深入理解Java并发之synchronized实现原理:https://blog.csdn.net/javazejian/article/details/72828483#%E7%90%86%E8%A7%A3java%E5%AF%B9%E8%B1%A1%E5%A4%B4%E4%B8%8Emonitor
java 并发编程-volatile、CAS、synchronize:
https://www.cnblogs.com/dosomethingyoulike/p/9344734.html