go的读写锁sync.RWMutex

9 篇文章 0 订阅
4 篇文章 0 订阅

有这样一个经典的读写锁问题,假设读锁和写锁之前互斥,读锁和读锁之间不互斥。现在做一个实验:
1、线程A加一个读锁 ,然后不释放;
2、然后线程B想加一个写锁,会被线程A的读锁阻塞;
3、然后有个线程C尝试去加一个读锁。

按照上面的步骤,步骤3 能加锁成功吗?

使用go语言的sync.RWMutex模拟这段代码,大概如下:

func main() {

	var flag int
	var rwLock sync.RWMutex

	go func() { //sessionA
		rwLock.RLock() //加读锁
		fmt.Println("session A", "flag=", flag)
	}()

	go func() { //sessionB
		time.Sleep(time.Second * 5)
		fmt.Println("sessionB try to get write lock")
		rwLock.Lock() //加写锁
		flag = 10
		fmt.Println("session B", "flag=", flag)
	}()

	go func() { //sessionC
		time.Sleep(time.Second * 8)
		fmt.Println("sessionC try to get read lock")
		rwLock.RLock() //加读锁
		fmt.Println("session C", "flag=", flag)
	}()

	time.Sleep(time.Second * 15)
	fmt.Println("end")

}

执行下上面的代码,我们会发现得出下面这样的结果。
在这里插入图片描述
很明显,线程B是没有上锁成功的,因为flag最后的值没有变,还是0;线程C也是没有上锁成功的,因为线程C里面的内容 fmt.Println(“session C”, “flag=”, flag) 没有执行成功。然后问题来了,发现了吗,线程B明明没有上写锁成功啊,为什么我的线程C就是上不了锁啊?毕竟线程A的读锁也不会和我的读锁互斥啊

下面一段读写锁的概念和图都转载自 小林coding的文章,原文请点击这里

读写锁根据实现的不同,可以分为「读优先锁」和「写优先锁」。

读优先锁期望的是,读锁能被更多的线程持有,以便提高读线程的并发性,它的工作方式是:当读线程 A 先持有了读锁,写线程 B 在获取写锁的时候,会被阻塞,并且在阻塞过程中,后续来的读线程 C 仍然可以成功获取读锁,最后直到读线程 A 和 C 释放读锁后,写线程 B 才可以成功获取读锁。如下图:
在这里插入图片描述
而写优先锁是优先服务写线程,其工作方式是:当读线程 A 先持有了读锁,写线程 B 在获取写锁的时候,会被阻塞,并且在阻塞过程中,后续来的读线程 C 获取读锁时会失败,于是读线程 C 将被阻塞在获取读锁的操作,这样只要读线程 A 释放读锁后,写线程 B 就可以成功获取读锁。如下图:
在这里插入图片描述
看了上面读优先锁和写优先锁的解释,我们可以明白,原来go中sync.RWMutex的实现,是基于写优先原则去实现的,线程C上不了锁就是因为它前面还有个线程B的写锁在等待,而写锁是优先于读锁的。

看了go的sync.RWMutex实现,那么其他的锁也是这么设计的吗?让我们一起去看看MySQL的MDL锁是怎么实现的?

一起做个实验,假设有一张student表长下面这样:
在这里插入图片描述

然后我们做下面几个操作:
1、线程A开启事务,然后查询student表,正常返回结果;在这里插入图片描述
2、线程B查询student表,也是正常返回结果;
在这里插入图片描述
3、线程C给student表添加字段,被阻塞了。这是因为MySQL中是有MDL(元数据)锁的,当对一个表做增删改查操作的时候,加 MDL读锁;当要对表做结构变更操作的时候,加 MDL 写锁。而写锁和读锁是互斥的,所以这里线程C想获取写锁,但被线程A的读锁阻塞了。
在这里插入图片描述
4、线程D再次执行查询语句,发现这时也被阻塞了。
在这里插入图片描述
到这里,我们应该也不难发现,MySQL的MDL锁也是写优先锁。其实也很好理解,像上面这个例子,我们在线程C中想加入一个字段s_adress,那么后面的线程D可能就是希望能查出这个新字段的。所以此时线程D被阻塞了,等线程C先执行完。

以上就是从go的读写锁引和MySQL的MDL锁结合在一起,对读写锁的一些思考和研究。我们知道了原来读写锁还有更细的划分 “读优先锁”和“写优先锁”。留个小问题,大家可以去测下MySQL中的行锁,即 FOR UPDATE (写锁)和 LOCK IN SHARE MODE(读锁) ,看下是读优先还是写优先呢?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值