Synchronized初步了解

*synchronized是什么?

​ synchronized是java关键字, 用来修饰代码块、实例方法、静态方法中。它能保证被修饰的代码块或方法在任意时刻只能被一个线程访问。只有该线程执行完解锁以后,才能被其他线程访问。

  • 用在代码块中,监视器对象可以是任意object对象

  • 用在实例方法中,监视器对象是当前对象this

  • 用在静态方法中,监视器对象是class对象

*Synchronized锁升级

JDK1.6之前,synchronize只有重量级锁,效率低下。但在jdk1.6之后,引入了偏向锁、轻量级锁等锁升级技术,使得synchronized的效率变得不错。

在内存中对象主要由对象头、对象体组成。对象头主要由Mark World ,指向类的指针组成。

Mark World:记录了对象信息(hahscode、分代年龄)和三种锁的信息,长度是64bit。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGYCjWLS-1630836007017)(D:\TyporaSet\截图2\816762-20210402144403198-1219531724-1627433291474.png)]

Epoch字段值:表示此对象偏向锁的撤销次数。默认撤销40次以上,表示此对象不再适用于偏向锁,当下次线程再次获取此对象时,直接变为轻量级锁。

无锁–>偏向锁–>轻量级锁–>重量级锁

(1)偏向锁–>轻量级锁

偏向锁适用于一个线程重复获取锁

当线程1尝试获取锁对象时,会在锁对象的Mark Wrod记录锁偏向的线程的ID

此后,线程再次获取锁的时候,需要比较当前线程的threadID和锁对象头中的threadID是否一致,如果一致,则线程直接获取锁对象;如果不一致,CAS尝试更改线程ID,CAS成功就获得锁了,CAS失败则升级为轻量级锁

(2)轻量级锁–>重量级锁
轻量级锁适用于少量线程竞争锁对象,而且线程持有锁的时间也不长

线程在获取轻量级锁时,会先把对象头MarkWord复制一份到栈中用于存储锁记录的空间,然后使用CAS把对象头中的内容修改为指向线程栈中存储的锁记录的引用;

如果CAS修改成功,则获得轻量级锁。如果CAS失败,那么线程一直自旋。如果自旋次数到了一定次数,轻量级锁就会膨胀为重量级锁。

(2)重量级锁

很多线程竞争锁,且线程持有时间长

重量级锁会把锁对象markworld的内容改为指向monitor的引用,并把除了拥有锁的线程都阻塞,但这些线程在进入阻塞队列前,还是会先尝试获取锁,如果获取不到才进入队列

注意:

锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态,因为markworld中有是否偏向锁的标志位

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wg89sQ5W-1630836007022)(D:\TyporaSet\截图2\816762-20210402160020148-1537048874-1627436619987.png)]

*synchronized重量级锁的工作原理?

每个Object对象都有一个对应的moniter对象:

1 ObjectMonitor() {
2     _count        = 0; //用来记录该对象被线程获取锁的次数
3     _waiters      = 0;
4     _recursions   = 0; //1、锁的重入次数
5     _owner        = NULL; //2、指向持有ObjectMonitor对象的线程 
6     _WaitSet      = NULL; //3、处于wait状态的线程,会被加入到_WaitSet
7     _WaitSetLock  = 0 ;
8     _EntryList    = NULL ; //4、处于等待锁block状态的线程,会被加入到该列表
9   }

1、synchronized修饰代码块

在需要同步的代码块开始的位置插入monitorentry指令,在同步结束的位置或者异常出现的位置插入monitorexit指令

当执行monitorEnter指令时,线程会尝试获取锁对象的monitor对象的所有权。(每一个monitor对象都有相应的锁计数器和指向持有该锁的线程的指针。)

如果锁计数器为0则获取成功,然后将指针指向当前线程,锁计数器加1。如果锁计数器大于0,则判断指针是否指向当前线程,如果指向当前线程则锁计数器加1。如果不是则获取失败,线程进入阻塞状态,直到锁被另一个线程释放为止

当执行monitorExit指令时,会将锁计数器减1。当锁计数器减为0时,则表明锁被释放。

2、synchronized修饰方法

JVM用ACC_SYNCHRONIZED标志指明该方法是同步方法。

如果该方法是同步方法,线程在执行方法前会先去获取对象的monitor对象,如果获取成功则执行方法代码,执行完毕后释放monitor对象。如果monitor对象已经被其它线程获取,那么当前线程被阻塞。

Synchronized为什么是非公平锁?

偏向锁:如果当前线程ID和markworld存储的不相等则CAS尝试更改线程ID,CAS成功就获得锁了,CAS失败则升级为轻量级锁。

轻量级锁:实际上也是通过CAS来抢占锁,只不过多了拷贝Markworld到lock Record的过程。抢占成功则获取锁,抢占失败就自旋,自旋失败一定次数后升级重量级锁。

重量级锁:通过monitor对象中的阻塞队列存储线程ID,但线程在进入队列前,还是会先尝试获取锁,如果获取不到才进入等待队列。

综上所述,synchronized无论处于哪种锁,线程都是先尝试获取,获取不到了才升级或放到到队列上的。所以是非公平的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值