文章目录
1.介绍
平时对Synchronized底层一些学习和整理,供后续查看使用
参考:深入分析Synchronized原理
synchronized原理
2.举例说明
2.1 同步问题
2.1.1 未加锁同步
1.例如下面,5个线程同时对一个资源进行相加操作,若没有增加同步控制,那么结果就会出现各种重复脏数据
@Slf4j
public class SynchronizedDemo {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread t = new Thread(new ThreadExe());
t.start();
}
}
}
@Slf4j
public class ThreadExe implements Runnable{
private Object o;
private static int num = 0;
public ThreadExe() {
}
public ThreadExe(Object o) {
this.o = o;
}
@SneakyThrows
@Override
public void run() {
for (int i = 0; i < 10; i++) {
Thread.sleep(new Random().nextInt(50));
log.info("数量增加:{}",num++);
}
}
}
2.1.2 加锁同步
2.若我们增加了一个同步锁,再执行,则结果就是依次相加,如图:
@Slf4j
public class SynchronizedDemo {
public static void main(String[] args) {
//增加同步锁对象
Object o = new Object();
for (int i = 0; i < 5; i++) {
Thread t = new Thread(new ThreadExe(o));
t.start();
}
}
}
@Slf4j
public class ThreadExe implements Runnable{
private Object o;
private static int num = 0;
public ThreadExe() {
}
public ThreadExe(Object o) {
this.o = o;
}
@SneakyThrows
@Override
public void run() {
synchronized (o){
for (int i = 0; i < 10; i++) {
Thread.sleep(new Random().nextInt(50));
log.info("数量增加:{}",num++);
}
}
}
}
2.2 字节码查看
1.工具下载 jclasslib bytecode viewer ,下载完成后重启。学习
下载好后,我们可以看看这个类的字节码结构,如图:
monitorexit指令出现了两次,第1次为同步正常退出释放锁;第2次为发生异步退出释放锁;
2.3 JVM层次查看
2.3.1 工具导入
相关工具包导入:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
2.3.2 对象结构打印
public class PersonDemo {
private Integer pId;
private String pName;
public PersonDemo() {
}
public PersonDemo(Integer pId, String pName) {
this.pId = pId;
this.pName = pName;
}
public Integer getpId() {
return pId;
}
public void setpId(Integer pId) {
this.pId = pId;
}
public String getpName() {
return pName;
}
public void setpName(String pName) {
this.pName = pName;
}
}
@Slf4j
public class SynchronizedDemo {
public static void main(String[] args) {
//对象锁
PersonDemo p = new PersonDemo();
//当没有加锁的时候,P这个对象存储内容打印
log.info("*********************未加锁***********************");
log.info(ClassLayout.parseInstance(p).toPrintable());
log.info("**********************加锁************************");
synchronized (p){
//调用sync关键字,对对象加锁
p.setpId(1);
p.setpName("kkl");
log.info(ClassLayout.parseInstance(p).toPrintable());
}
}
}
第一二行是java对象头的Mark Word共占8个字节
第三行是java对象头的class Pointer类指针,本来也是占8个字节,因JDK默认开启类指针压缩所以占4个字节,总共12个字节,又因12不能被8整除,所有最后又加了四个空字节
第四行是实例数据
从上图可知,第一行,最初是01 00 00 00,但当使用synchronized关键字后,会对对象里的对象头进行修改
3.锁升级过程
无锁 -->偏向锁–>自旋锁(轻量级锁)–>重量锁
锁状态:
-
当一个对象刚开始new出来时,该对象是
无锁状态
。此时偏向锁位为0,锁标志位01 -
如果有线程上锁:
指的就是把markword的线程ID改为自己线程ID的过程
- 如果有线程竞争:
撤销偏向锁,升级轻量级锁
线程在自己的线程栈生成LockRecord,用CAS操作
将markword设置为指向自己这个线程的LockRecord的指针,设置成功者得到锁
- 如果竞争加剧
竞争加剧:有线程超过10次自旋, -XX:PreBlockSpin,或者自旋线程数超过CPU核树的一半,1.6之后,加入自适应自旋adapative self spinning,JVM自己控制;
升级重量级锁: 向操作系统升级资源,等待操作系统的调度,然后再映射会用户空间;
偏向锁和自旋锁都是用户态的,不需要和OS内核打交道
4.其他
4.1 HotSpot
像我们一般执行java -version时,会看到我们常用的虚拟机是啥,如图:
VM是虚拟机,总的来说是一种标准规范,虚拟机有很多实现版本。主要作用就是运行java的类文件的。
而HotSpot是虚拟机的一种实现,它是sun公司开发的,是sun jdk和open jdk中自带的虚拟机,同时也是目前使用范围最广的虚拟机。
HotSpot,顾名思义,它是基于热点代码探测的,有JIT即时编译功能,能提供更高质量的本地代码。
二者区别是一个是标准,一个是实现方式。
java对象在内存中的结构(HotSpot虚拟机)
JVM 技术内幕——HotSpot VM
JOL工具分析java对象大小
如何计算Java对象的大小
JVM—对象内存布局(jol插件验证)
synchronized关键字深入总结