多线程第四节_synchronized

1. 设计同步器的意义

	多线程中,有可能会出现同一个线程访问同一个共享资源,我们称这个资源为临近资源,这种资源可以使对象,变量,文件。由于线程访问的过程不可控,所以需要采用同步机制来协同对象可变状态的访问

1.1 如何解决线程并发安全问题

	序列化访问临界资源。在同一时刻只有一个线程可以访问临界资源,也称作同步互斥访问。java中提供了俩种方式实现同步互斥访问 synchronized,lock;

2. synchronized原理详解

	synchronized内置锁是一种对象锁,锁的对象而非引用,作用力度是对象,可重入;加锁的方式有三种,1同步实例方法,锁当前实例对象;2同步类方法,锁的当前类对象;3同步代码块,锁的是括号里的对象

2.1 synchronized的底层原理

	synchronized是基于jvm内置锁实现,通过内部的monitor(监视器锁)实现,基于进入退出monitor对象实现方法与代码同步,monitor的实现依赖底层操作系统的mutex lock(互斥锁)实现,它是一个重量级锁,性能低,当然jvm1.5后进行了优化,如所粗化,锁消除,轻量级锁,偏向锁,适应性自旋等减少锁开销。内置锁的性能基本和lock持平。
     synchronized关键字被编译成字节码后会被翻译成monitorenter和monitorexit俩条指令分别在同步块逻辑代码的其实位置与结束位置

1655796719467

1655796739636

2.2 monitor监视锁

任何一个对象都有一个monitor与之关联,当一个monitor被持有后,他将处于锁定状态;
    monitorenter:每个对象都是一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行 monitorenter指令时尝试获取monitor的所有权,过程如下他的执行过程如下:
    a 如果monitor的进入数为0 ,则该线程进入monitor,然后进入数设置为1,该线程既为monitor的所有者
    b 如果线程已经占有了monitor,只是重新进入,则monitor进入加1
    c 如果其他线程占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,在尝试获取monitor的所有权
 	
    monitorexit:执行monitorexit的线程必须对应的monitor持有者,执行指令时,monitor的进入数减一,如果减一后进入数为0,则线程退出monitor,不再是这个monitor的持有者,其他被这个monitor阻塞的线程可以尝试进入获取这个monitor的所有权。
    
    通过上面两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来 完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则 会抛出java.lang.IllegalMonitorStateException的异常的原因。
看一个案例

1655797449315

1655797458283

1655797537922

2.3 什么是monitor

	可以理解成一个同步工具,或者一种同步机制,通常被描述为一个对象。与一切皆对象一样,所有的java对象天生的monitor,每一个java对象都有成为monitor的潜质,因为java设计中,每一个java对象自产生开始就带了一把看不见的锁,它叫内部锁或者monitor锁,也就是长说的synchronize对象锁,markword的锁标识为10,其中指针指向的是monitor对象的起始地址。在Java虚拟机(HotSpot)中,Monitor是由ObjectMonitor实现的,其主要数据结构如下(位于 HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的):

1655798994178

1655799035294

3. 对象的内存布局

	hospot虚拟机中,对象在内存中存储的布局分为三个区域,对象头hearder,实例数据instance data,对其填充 padding
    1 对象头,比如hash吗,对象所属的年代,对象锁,锁状态标志,
    2 实例数据,存放类的属性数据信息
    3 对其填充,虚拟机要求对象起始地址必须是8字节的整数倍,只是为了对齐字节

1655799213322

3.1 对象头

	hotspot虚拟机的对象头包含俩部分信息,Markword,存储对象自身的运行时数据,如哈希吗,gc分代年龄,锁状态标志,线程持有的锁,偏向线程id,偏向时间戳等它是实现轻量级锁和偏向锁的关键

1655799345713

1655799356574

1655799381957

3.2 对象头分析工具

1655799398386

3.3 锁膨胀升级过程

	锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重 量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。从JDK 1.6 中默认是开启偏向锁和轻量级锁 的,可以通过-XX:-UseBiasedLocking来禁用偏向锁。下图为锁的升级全过程:

1655799479773

3.3.1 偏向锁

1655799504256

3.3.2 轻量级锁

1655799519680

3.3.3 自旋锁

1655799538816

3.3.4 锁消除

1655799599089

3.3.5 逃逸分析

1655799618636

1655799686811

1655799699684

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章