首页 💎并发编程_synchronized
文章
取消

💎并发编程_synchronized

Synchronized

1.使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
·同步方法
		JVM源码:在class类文件中的访问标识符中 标识为 ACC_SYNCHRONIZED
		普通方法-锁的当前对象
		静态方法-锁的是当前类.class
·同步代码块
		原理:
		从monitor-entry进入 再从monitor-exit出来一个正常出口和一个异常出口
		线程执行到monitor-entry指令后 尝试获取对象对应的monitor所有权 
		JVM中每个对象都会有一个'监视器',监视器和对象一起创建、销毁。监视器相当于一个用来监视这些线程进入的特殊房间,其义务是保证(同一时间)只有一个线程可以访问被保护的临界区代码块
		·Hotspot虚拟机中,监视器是由C++类ObjectMonitor实现的
			组成:Cxq:竞争队列(Contention Queue),所有请求锁的线程首先被放在这个竞争队列中
				 EntryList:Cxq中那些有资格成为候选资源的线程被移动到EntryList中
                   WaitSet:拥有Monitor的线程在调用wait()方法之后将被阻塞然后该线程将被放置在WaitSet中
				 owner:获得到minitor的对象会进入owner

2.原理:

1
2
3
4
5
6
7
8
9
10
·对象:
	1.对象头
			1.1:Mark-Word(32位):存储锁的信息、GC信息
					无锁状态:    hashcode(25位)、      分代年龄(4位)、偏向位(1位)[0]、锁标记位(2位)[01]
					偏向锁态: 线程id(23位)、epoch(2位)、分代年龄(4位)、偏向位(1位)[1]、锁标记位(2位)[01]
					轻量级锁:           指向栈中锁记录的指针(30位)、			     锁标记位(2位)[00]
					重量级锁:           指向重量级锁的指针(30位)、			          锁标记位(2位)[10]			  1.2:ClassPoint(32位) 
			1.3:数组长度(如果是数组对象)		
	2.实例数据
	3.填充

3.锁升级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1.无锁状态: 普通对象
👇👇👇👇👇

2.偏向锁态:(无线程竞争锁)
		-获取偏向锁大多数情况下 锁不存在竞争并且总是由一个线程多次获取 为了让线程获得所得代价更低 就引入偏向		 锁让线程访问同步代码块得到锁后 会在对象头和栈帧的锁记录中存储偏向锁的线程ID 
		-再次获取偏向锁:获取锁时只需要看MarkWord中是否存在 当前线程的偏向锁 存在的话 说明是同一个线程获取锁 			如果不存在 则再看MarkWord中偏向锁的标识是否设置为1 如果有则用cas把对象头的偏向锁指向当前线程 如果			没有则使用cas竞争
		-释放偏向锁:当出现锁的竞争的时候 就要撤销 撤销时要等到全局的安全点(此时无执行的字节码文件) 暂停拥有的偏向锁线程  然后检查拥有的偏向锁线程是否存活 不是存活状态 则设置为 ‘无锁状态’ 是存活状态 拥有偏向锁的栈会被执行 遍历偏向锁对象的锁记录 栈中的锁记录 和对象头的markword 要么重新偏向与其他线程 要么恢复到 无锁或者标记对象不合适作为偏向锁 最后唤醒暂停线程
		-java6、7默认开启偏向锁 只不过要等待几秒钟才能启动 如果明确有锁竞争 则可以关闭偏向锁 直接进入轻量级锁
👇👇👇👇👇
3.轻量级锁:
		-轻量级锁的加锁:
			先撤销偏向锁
			在执行同步代码块前 在当前线程的栈的栈帧中 创建锁记录 然后把对象头中的markword复制到 锁记录中
			然后用cas将对象头中的markword替换成指向所记录的指针 如果成功则表示获取到锁 否则表示其他锁竞争
		-轻量级锁的解锁:
			用cas把锁记录的markword信息复制到对象头的markword中
        -升级为重量级锁:当自旋次数超过 10次或者线程数超过cpu核数的一半   
👇👇👇👇👇	
4.重量级锁:
		处于重量级锁状态下 没获取到锁的线程 会被阻塞 进入阻塞队列 线程挂起 不占用cpu资源

		·重量级锁的原理:JVM中每个对象都会有一个'监视器',监视器和对象一起创建、销毁。监视器相当于一个用来监视这些线程进入的特殊房间,其义务是保证(同一时间)只有一个线程可以访问被保护的临界区代码块
		·Hotspot虚拟机中,监视器是由C++类ObjectMonitor实现的
			组成:Cxq:竞争队列(Contention Queue),所有请求锁的线程首先被放在这个竞争队列中
				 EntryList:Cxq中那些有资格成为候选资源的线程被移动到EntryList中
                   WaitSet:某个拥有ObjectMonitor的线程在调用Object.wait()方法之后将被阻塞,然后该线程将被放置在WaitSet链表中
				 获得到minitor的对象会进入owner
		·重量级锁会发生线程的上下文切换,从用户态切换到内核态。系统调用是内核态为用户态进程提供的Linux内核态下互斥锁的访问机制,系统调用就会从用户态切换到内核态,这种切换是需要消耗很多时间的,而JVM重量级锁使用了Linux内核态下的互斥锁mutex。 ![1657965926287](C:\Users\User\AppData\Roaming\Typora\typora-user-images\1657965926287.png)

4.锁之间的比较

1
2
3
4
5
6
7
8
9
偏向锁:
			适合只有一个线程访问同步代码块 加锁解锁不要耗费资源
			但是 如果存在多个线程的锁竞争则会带开 偏向锁撤销的消耗
轻量级锁:
			适合追求响应比的场景 同步代码块执行速度很快 线程不会阻塞
			但是 如果始终得不到锁竞争的线程 则会空耗cpu资源
重量级锁:
			适合吞吐量高的场景 同步代码块时间长 线程之间竞争不自选 而是挂起 不耗费资源
			但是 线程会阻塞 等待时间长 响应比低

5.CAS和Synchronized的区别

1
2
1.加锁的开销:死锁 加锁 解锁 线程切换 内核态到用户态的切换 线程阻塞等待时间长--追求吞吐量
2.CAS的空循环占cpu资源  线程不会阻塞 --追求相应比
重量级锁图

1658024706125

1658024952487

本文由作者按照 CC BY 4.0 进行授权

题型归类🔍_模拟问题 字符串

💎并发编程_volatile