首页 > 其他分享 >多线程-sychronized锁膨胀

多线程-sychronized锁膨胀

时间:2023-05-22 14:35:38浏览次数:34  
标签:lock 是否 lockrecord 线程 sychronized 膨胀 多线程 轻量 偏向

sychronized

什么是cas

  1. cas的定义:在操作系统中,CAS通常代表“Compare And Swap”,它是一种原子操作,用于解决并发访问的问题。具体地说,CAS操作会比较并交换一个内存位置的值,只有当内存位置的值与期望的值相等时,才会将新值写入该位置。如果内存位置的值与期望的值不相等,则说明这个内存位置已经被其他线程修改,此时CAS操作不会执行任何修改,并返回失败状态。
  2. cas涉及的指令:cpmxchg,他通常和自旋锁(spin lock)配合使用,这个自旋锁在os/c++中实现略有不同具体如下,看os就行
  3. linux内核下spin 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,判断锁是否是否被人持有-》如果没人持有看有没有排队的,若有人排队自己也去排队;如果锁被人持有了则加锁失败,自己去入队,如果自己是head,则自选取再次拿锁尝试)

多线程-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,如果后三位是001则代表无锁,下面是对应的几种情况

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

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

  1. 轻量锁 00,一个64位,前62位是一个指针,看后两位即可
  2. 重新加锁和重入锁的区别

偏向锁膨胀的具体过程,结合c++代码

  1. 偏向锁的获取过程,首先有一个锁对象lock,在当前线程下创建lockrecord,然后判断是否锁是自己持有,是否过期,然后拿到锁,这就是偏向锁,当第一次之后获取,比如第二次获取,还是会建立lockrecord,判断是否自己持有是否过期,如果满足就拿到锁不需要cas,所以性能最好,释放的过程就是从栈中找到对应的lockrecord释放掉
  2. 轻量锁:假设t1释放了偏向锁,t2进来拿锁,此时需要先进行偏向撤销(简单来说,撤销偏向,置成无锁001)
    其内存变化如下:(看c++文件重新写)
  3. 此时如果t2释放锁,t3来了,此时t3先生成无锁的markword 00,然后lockrocrd中displacedword 设置成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

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

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

1.是偏向锁,获取当前线程id,看是否偏向自己,如过偏向自己就拿到锁;如果没有则看是否关闭偏向锁,如果关闭则偏向锁升级为轻量锁;如果没关闭,看是否过期或者偏向别人,无论哪种情况都重新创建一个偏向自己的mark,通过cas设置对象头,成功就获得锁,失败就升级为轻量锁;

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

不恋尘世浮华,不写红尘纷扰

标签:lock,是否,lockrecord,线程,sychronized,膨胀,多线程,轻量,偏向
From: https://blog.51cto.com/u_12550160/6323874

相关文章

  • nodejs多线程,真正的非阻塞
    干货:收藏:http://cnodejs.org/topic/518b679763e9f8a5424406e9node从他推出至今,充满赞美和饱受诟病的都是其单线程模型,所有的任务都在一个线程中完成(I/O等例外),优势的地方自然是免去了频繁切换线程的开销,以及减少资源互抢的问题等等,但是当nodejs面对cpu密集型模型的时候就力不从心了......
  • NET 高级编程知识--多线程async/await
    NET高级编程知识一.多线程async/awaitasync:的价值1.降低了线程的数量;2.降低了cpu的负载;3.asnync/await提高了吞吐,只负责发命令,然后就忙别的事去了,不需要等待,事完成前不浪费资源,完成后在来线程处理,还能复用线程,不开新线程。asnyn:并发不高,线程较多,cpu开销不大,使用了DAM异......
  • 多线程
    进程和线程的基本术语进程:一个程序执行时,系统会给它划分一些内存区域,这就是一个进程线程:一个进程包含多个线程,java程序至少至少包含三个线程,main()主线程、gc()垃圾回收线程和异常处理线程并行:并发:一个CPU执行多个任务,用于商城中的秒杀多线程可以提高程序的相应,对图形化应用......
  • 多线程
    什么是多线程?首先要了解进程,一个进程可以启用多个线程。进程:1.可以看做一个公司,线程就是公司的员工。2.进程A和进程B的内存资源独立不共享3.进程是线程的执行单元线程:1.线程A与线程B之间堆内存和方法区共享,栈是独立的,一个线程一个栈。......
  • 数据分享|R语言零膨胀泊松回归ZERO-INFLATED POISSON(ZIP)模型分析露营钓鱼数据实例估计
    全文链接:http://tecdat.cn/?p=26915最近我们被客户要求撰写关于零膨胀泊松回归的研究报告,包括一些图形和统计输出。零膨胀泊松回归用于对超过零计数的计数数据进行建模。此外,理论表明,多余的零点是通过与计数值不同的过程生成的,并且可以独立地对多余的零点进行建模。因此,zip模型......
  • 【Python】多进程 多线程
    1.进程Process1.1多进程#-*-coding:UTF-8-*-"""#计算8的20次方"""importtimeimportosfrommultiprocessingimportProcessimportosimporttimedeflong_time_task(i):print('子进程:{}-任务{}'.format(os.getpid()......
  • JAVA基础(多线程Thread和Runnable的使用区别
    [color=red][size=x-large]两种定义方式[/size][/color]定义方式一:classTestThreadextendsThread{publicvoidrun(){........................}}Threadt=newTestThread();t.run()//或者t.start();定义方式二:Threadt=newRunnabl......
  • Qt ffmpeg yolov5 tensorrt 高性能部署,使用tensorrt推理yolov5模型,封装成了dll, 支
    Qtffmpegyolov5tensorrt高性能部署,使用tensorrt推理yolov5模型,封装成了dll,支持多窗口多线程推理,本项目为4窗口版,各个窗口支持识别类别,阈值,roi区域等设置。算法支持onnxruntime,tensorrt推理,以及推理加deepsort,bytetrack和kcf多目标跟踪。ID:353200676908443403......
  • Python多线程并发通用模板
    多线程可以同时处理多个任务,支持并发处理,从而提高系统的并发能力。多线程爬虫的好处主要有提高爬取效率、提高稳定性、节省资源等。总之,多线程爬虫可以提高爬取效率、稳定性和资源利用率,是一种更加高效、可靠的爬虫实现方式。多线程爬虫并行可以提高爬虫的效率,具体实现方法如下:......
  • 5月15日c++小语法右值引用,lambda表达式,和多线程
    c++中有一个东西叫做左值引用和右值引用,因为面向对象语言中有很多封装好的自定义类型容器,而这些容器又不像内置类型那样传值方便,有时候可能会有很大的深拷贝浪费于是有了左值引用:在函数传参时方便的传引用避免了传复杂的指针,而在返回之上直接传引用减少了不必要的深拷贝.而右......