一文带你全面理解各种锁机制

举报
dayu_dls 发表于 2021/05/21 09:51:14 2021/05/21
【摘要】 并发访问共享资源,如果不加锁,可能会导致数据不一致问题,通常为了解决并发访问问题,我们都会在访问共享资源之前加锁,保证同一时刻只有一个线程访问。下面我们用问答的方式说明下各种并发锁的概念、优缺点及其应用场景。1、什么是互斥锁和自旋锁,各有什么优缺点?互斥锁和自旋锁是最底层的两种锁,其他的很多锁都是基于他们的实现。当线程A获取到锁后,线程B再去获取锁,有两种处理方式,第一种是线程B循环的去尝试...

并发访问共享资源,如果不加锁,可能会导致数据不一致问题,通常为了解决并发访问问题,我们都会在访问共享资源之前加锁,保证同一时刻只有一个线程访问。下面我们用问答的方式说明下各种并发锁的概念、优缺点及其应用场景。
1、什么是互斥锁和自旋锁,各有什么优缺点?
互斥锁和自旋锁是最底层的两种锁,其他的很多锁都是基于他们的实现。当线程A获取到锁后,线程B再去获取锁,有两种处理方式,第一种是线程B循环的去尝试获取锁,直到获取成功为止即自旋锁,另一种是线程B放弃获取锁,在锁空闲时,等待被唤醒,即互斥锁。互斥锁会释放当前线程的cpu,导致加锁的代码阻塞,直到线程再次被唤醒。互斥锁加锁失败时,会从用户态陷入到内核态,让内核帮我们切换线程,存在一定的性能开销。
    (1)当线程加锁失败,内核会把线程的状态由“运行”设置为“睡眠”,让出cpu
    (2)当锁空闲时,内核唤醒线程,状态设置为“就绪”,获取cpu执行
而自旋锁会自用户态由应用程序完成,不涉及用户态到内核态的转化,没有线程上下文切换,性能相对较好。自旋锁加锁过程:
    (1)查看锁的状态
    (2)锁空闲,获取锁,否则执行(1)
自旋锁会利用cpu一直工作直到获取到锁,中间不会释放cpu,但如果被锁住的代码执行时间较长,导致cpu空转,浪费资源。
2、什么是读写锁?
读写锁由读锁和写锁组成。读锁又称为共享锁,S锁,写锁又称为排它锁、X锁,在mysql的事务中大量使用。写锁是独占锁,一旦线程获取写锁,其他线程不能获取写锁和读锁。读锁是共享锁,当线程获取读锁,其他线程可以获取读锁不能获取写锁。因为并发数据读取并不会改变共享数据导致数据不一致。读写锁把对共享资源的读操作和写操作分别加锁控制,能够提高读线程的并发性,适用于读多写少的场景。
3、什么是读优先锁、写优先锁、公平读写锁?
读优先锁希望的是读锁能够被更多的线程获取,可以提高读线程的并发性。线程A获取了读锁,线程B想获取写锁,此时会被阻塞,线程c可以继续获取读锁,直到A和c释放锁,线程B才可以获取写锁。如果有很多线程获取读锁,且加锁的代码执行时间很长,就到导致线程B永远获取不到写锁。
写优先锁希望的是写锁能够被优先获取。线程A获取了读锁,线程B想获取写锁,此时会被阻塞,后面获取读锁都会失败,线程A释放锁,线程B可以获取写锁,其他获取读锁的线程阻塞。如果有很多写线程获取写锁,且加锁的代码执行时间很长,就到导致读线程永远获取不到读锁。
上面两种锁都会造成【饥饿】现象,为解决这种问题,可以增加一个队列,把获取锁的线程(不管是写线程还是读线程)按照先进先出的方式排队,每次从队列中取出一个线程获取锁,这种获取锁的方式是公平的。
4、什么是乐观锁和悲观锁?
乐观锁是先修改共享资源,再用历史数据和当前数据比对验证这段时间共享数据有没有被修改,如果没有被修改,那么更新数据,如果有其他线程更新了共享资源,需要重新获取数据,再更新,验证,循环往复的重试,直到更新成功。所以当数据更新操作比较频繁,数据冲突的概率就会比较大,重试的次数就会多,浪费CPU资源。乐观锁其实全程没有加锁,也叫无锁编程,所以针对读多写少的场景,并发性能较高,典型的实现MVCC,mysql中会使用MVCC构建一致性读来保证可重复读。悲观锁是在访问共享资源之前统统加锁。当并发冲突概率较高时,乐观锁不在适用,悲观锁就排上用场。互斥锁、自旋锁都是悲观锁的实现。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。