当前位置:首页 > 自我学习 > 正文内容

互斥锁和信号量区别(互斥锁性能消耗)

2023-04-21 10:48:05自我学习1

互斥锁性能消耗

每个dble节点内存中都维护metadata。metadata是每个节点从后端mysql查询解析出来的,分库分表多个表的时候,会做分片一致性校验。

有两个层级的锁来使操作相同表的DDL串行执行

1. 单节点时本地锁

内存中本地锁,单进程内每个DDL sql串行去校验锁,锁本身是按照表的粒度组织的。

2. 多节点时zk节点互斥

在本地锁上新增一层 zk中的互斥锁,执行DDL前选获取本地锁,再检查zk中的节点,保证操作相同表的DDL在多个dble节点之间串行执行。DDL执行成功后还要通知其他节点更新各自维护的metadata。

互斥锁用法

、spinlock 简介

自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,不断尝试获取锁,直到获取到锁才会退出循环

二、自旋锁与互斥锁的区别

自旋锁与互斥锁类似,它们都是为了解决对某项资源的互斥使用,在任何时刻最多只能有一个线程获得锁

对于互斥锁,如果资源已经被占用,调用者将进入睡眠状态

对于自旋锁,如果资源已经被占用,调用者就一直循环在那里,看是否自旋锁的保持者已经释放了锁

三、自旋锁的优缺点

自旋锁不会使线程状态发生切换,不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换,影响性能

如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程长时间循环等待消耗CPU,造成CPU使用率极高

互斥锁实现原理

mutex锁的原理如下

1.不同于信号量,mutex需要谁拿的锁谁来释放锁。

2.不同于自旋锁,mutex临界区允许睡眠。

3.不同于自旋锁,mutex在拿锁时若锁被别人持有,会根据锁的持有者是否正在运行来决定是乐观自旋或是睡眠等待。 atomic_long_t owner; //owner中记录了锁的持有者的task_struct地址,且低3bit记录了锁的状态

spinlock_t wait_lock; //用来保护wait_list链表

struct list_head wait_list; //等待链表,等着拿锁的进程会被记录在此list上,操作wait_list需要wait_lock的保护owner成员可被认为是mutex锁的本体,此成员为空则说明锁未被持有,非空则说明锁被持有。实际上owner被划分为两个域——task field和flags field,分别是63bit至3bit存放锁的持有者的task_struct地址,2bit至0bit存放锁的状态。其中bit0表示wait_list是否为空,bit1和bit2用来实现handoff机制。

互斥锁会产生死锁吗

  死锁deadlocks(死锁)由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。一种情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源。例如,如果线程A锁住了记录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发生了死锁现象。

互斥锁使用场景

python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。 直到一个线程所有的acquire都被release,其他的线程才能获得资源。这里以例1为例,如果使用RLock代替Lock,则不会发生死锁!

互斥锁性能消耗很小

首先,Java中synchronized可以实现对对象加互斥锁。

其次,我们来聊一聊synchronized互斥锁的实现原理。Java虚拟机中,synchronized支持的同步方法和同步语句都是使用monitor来实现的。每个对象都与一个monitor相关联,当一个线程执行到一个monitor监视下的代码块中的第一个指令时,该线程必须在引用的对象上获得一个锁,这个锁是monitor实现的。在HotSpot虚拟机中,monitor是由ObjectMonitor实现,使用C++编写实现,具体代码在HotSpot虚拟机源码ObjectMonitor.hpp文件中。

查看源码会发现,主要的属性有_count(记录该线程获取锁的次数)、_recursions(锁的重入次数)、_owner(指向持有ObjectMonitor对象的线程)、_WaitSet(处于wait状态的线程集合)、_EntryList(处于等待锁block状态的线程队列)。

当并发线程执行synchronized修饰的方法或语句块时,先进入_EntryList中,当某个线程获取到对象的monitor后,把monitor对象中的_owner变量设置为当前线程,同时monitor对象中的计数器_count加1,当前线程获取同步锁成功。

当synchronized修饰的方法或语句块中的线程调用wait()方法时,当前线程将释放持有的monitor对象,monitor对象中的_owner变量赋值为null,同时,monitor对象中的_count值减1,然后当前线程进入_WaitSet集合中等待被唤醒。

互斥锁可能导致线程阻塞

线程同步的四种常见方式是:

1. 互斥锁:通过在访问共享资源前获取互斥锁,保证只有一个线程可以访问该共享资源。

2. 条件变量:当多个线程需要等待某些条件满足时,使用条件变量来阻塞这些线程,并在条件满足时唤醒它们继续执行。

3. 信号量:用于控制并发访问数量的计数器。当某个线程需要占用共享资源时,它会尝试获得信号量。如果当前可用信号量数为0,则被阻塞;否则成功获取后就可以访问了。

4. 屏障(barrier):等待所有参与者都达到某一点再同时开始执行下一步操作的机制。屏障可分为简单屏障和循环屏障两种类型。

本网站文章仅供交流学习 ,不作为商用, 版权归属原作者,部分文章推送时未能及时与原作者取得联系,若来源标注错误或侵犯到您的权益烦请告知,我们将立即删除.

本文链接:https://www.xibujisuan.cn/98789525.html