多线程-sychronized锁膨胀

发布时间:2023-05-23 09:31:09

什么是cassychronized?

  1. cas的定义:在操作系统中,cas通常代表“Compare And Swap它是一种解决并发访问问题的原子操作。具体来说,CAS操作将比较和交换内存位置的值。只有当内存位置的值等于预期值时,新值才会写入该位置。如果内存位置的值与预期值不相等,则表明内存位置已被其他线程修改,CAS操作不会进行任何修改,并返回失败状态。
  2. cas所涉及的指令:cpmxchg,他通常和自旋锁在一起(spin lock)这个自旋锁在os//c++实现略有不同,具体如下:看os
  3. spininux内核下的linux lock
typedef struct spinlock {        union {                atomic_t count;        };} spinlock_t;
  1. c++中spin lock
#include <atomic>  // class头文件 SpinLock {public:    SpinLock() { flag.clear(std::memory_order_release); }        void lock() {        while (flag.test_and_set(std::memory_order_acquire)) ;  // 忙等待    }        void unlock() {        flag.clear(std::memory_order_release);    }private:    std::atomic_flag flag;};

在多线程序中,CAS可以用来保证数据的一致性和正确性,防止竞争条件等问题。因此,CAS操作是许多并发编程框架和实现中常用的技术之一。

什么是公平锁,什么是公平锁,先说reentrantlock。
  1. 结论:公平和非公平锁的区别在于抢锁前是否排队。一旦你进入团队,你将永远排队
  1. 公平锁紧逻辑:线程进入-调用lock方法-tryacquire 获取当前线程,判断锁的状态是否为0,锁是否被持有——如果没有人持有,看看是否排队,如果有人排队;如果锁被持有,锁失败,自己进入队,如果你是手持,选择再次尝试锁)

多线程-sychronized锁膨胀_无锁

  1. 非公平锁抢锁逻辑:线程进入-调用lock方法-然后tryacquire(这里的具体行为如下:获取当前线程-获取锁状态是否为0-”,判断是否被持有。如果被持有,就会自旋等待。如果你成功了,你就会得到锁。如果你失败了,进入队列。这个时候,你会看一个节点是不是head。如果你选择一次试图拿到锁,
  2. 这是别人画的图片,讲述整个过程
  3. 队列:刚才说到入队,队列的数据结构如下
public class Node{volatile Node prev;volatile Node next;volatile Thread thread;}
关于synchronized是否公平锁定

1. 正如上面所说,是否公平取决于入队前是否抢锁,入队后总是排队

锁的基本知识

首先,介绍锁和内存分布

  1. 无锁:分为101 无锁可以偏向或偏向锁。如果前52个字符不是0,则表示偏向锁。前52位是偏向线程id。如果后3位是001,则表示无锁。以下是相应的情况

多线程-sychronized锁膨胀_公平锁_02

多线程-sychronized锁膨胀_加锁_03

  1. 轻量锁 00,一个64位,前62位是指针,看后两位
  2. 重新加锁和重新加锁的区别
  3. 偏向于锁膨胀的具体过程,结合c++代码
  4. 偏向锁的获取过程中,首先有一个锁对象lock,在当前线程下创建lockrecord,然后判断锁是否自己持有,是否过期,然后拿到锁。这就是偏向锁。第一次获得,比如第二次获得,还是会建立lockrecord,判断自己是否持有过期。如果满意,不需要cas就能拿到锁,所以性能最好。释放的过程是从栈中找到相应的lockrecord释放
  5. 轻量级锁:假设T1释放了偏向锁,T2进来拿锁,此时需要先撤销偏向(简单来说,撤销偏向被放置为无锁001),其内存变化如下:(见c++文件重写)
  6. 这时,如果t2释放锁,t3来了,t3先生成无锁markword 然后displacedword在lockrocrd中 设置为markword,然后cas判断当前对象头和内存中对应的markword是否相等。如果相等,将对象头中的markword修改为指向lockrecord的指针,同时,将对象头中markword的后两位改为00;

多线程-sychronized锁膨胀_无锁_04

多线程-sychronized锁膨胀_无锁_05

 

4.  轻量锁膨胀为重量锁:T3修改前,T4进来完成T3要完成的操作,T3加锁失败后会膨胀,涉及到 pthredmutex;
  1. 批量偏差涉及连续偏差取消20次和40次的两个阈值:结论:当取消偏差超过锁阈值时 20 次后,jvm 会这样想,我是不是偏向错了,因此,在锁定这些对象时,它会再次偏向t2;当偏向锁被取消40次时,认为对象设计有问题;然后JVM将取消对应对象类别的所有对象;新的实例对象也不可偏向
  2. 锁膨胀流程图:

多线程-sychronized锁膨胀_公平锁_06

大概逻辑如上图,这里的文字版;

获得锁的对象lockeee->创建lockrecord->让lockrecordobj关联lockee->获取lockee的头信息-此时判断是否偏向锁(这里有一些细节,比如初始化01,看了一遍也记不住)

1.是偏向锁,获取当前线程ID,看看是否偏向自己,如果偏向自己,看看是否关闭偏向锁,如果关闭偏向锁升级为轻锁;如果不关闭,看看是否过期或偏向他人,创建偏向自己的马克,设置对象头,成功获得锁,升级为轻锁;

2.不偏向锁升级为轻量锁

不爱世间浮华,不写红尘纷扰

上一篇 leetcode 367.有效的完全平方数
下一篇 Redis开发规范——键值设计

文章素材均来源于网络,如有侵权,请联系管理员删除。

标签: Java教程Java基础Java编程技巧面试题Java面试题