推荐阅读:

  • 我总结了72份面试题,累计3170页,斩获了30+互联网公司offer(含BATJM)

  • 2020首战告捷,这份Java面试神技Plus版,让我成功拿到了阿里、京东、字节跳动等大厂offer

  • 膜拜!阿里内部都在强推的K8S(kubernetes)学习指南,不能再详细了

年轻人,醒醒吧!此时不搏何时搏!本文主要讲一下常见的CAS理论。再者就是说一下锁的分类,什么乐观锁啊,悲观锁、重入锁等等。这篇文章要一网打尽,都介绍一下。

把CAS按在地上摩擦

中文名:比较并交换

英文名:Compare And Swap

英文缩写:CAS

他是一种无锁化基于乐观锁思想实现的算法,目的是在不使用锁的情况下实现多线程之间的共享数据同步。在Java的java.util.concurrent包中的原子类(不是原子弹)就是基于CAS的实现的。在CAS的算法世界中,存在三大护法:value(要更新的变量)、expected(预期值)和new(要新写入的值)。下面画图说明CAS是如何实现不加锁的情况下协调多线程同步共享数据的:

解释一下:当A、B两个线程都操作value值时,线程A如果一切顺利,会在进行预期值与内存值做比较且相等,这个动作是原子化操作,这时候执行原子的修改value值的操作。修改完成后,B线程也来修改,发现有敌情,只好原地循环等待,直到条件符合时才进行内存值的操作。还有一点要注意的是,比对和修改两个动作都是原子的,但是原子操作 + 原子操作 != 原子操作。多线程高并发,搞的额头没头发。

理想与现实的差距就是这么大…

锁的分类

乐观锁与悲观锁

这二位其实并不是实际存在的锁,仅仅是对锁的抽象定义。乐观锁的目的就是不加锁,从而提升效率。这一思想在Java以及基于数据库实现的乐观锁中都有实践。

在乐观锁的概念里,认为所有的数据都是为我当前线程服务的,在我使用的过程中不会有别的线程修改我的数据(哼,想多了),但是为了保险起见,在更新目标数据的时候还是要做一次对比,即前面说的CAS过程。不过乐观锁是思想,CAS是算法。搞清楚这个就行了。

在悲观锁的概念里,跟乐观锁恰好相反,它的核心是“总有刁民想害朕”即所有线程都可能修改自己持有的数据。因此在读取数据的时候就赶紧上锁,其他人都别想动我的宝贝!大家都立正,一个一个按顺序来。比如前面写到的Synchronized和后面将要写的Lock接口,还有就是基于数据库的悲观锁:select xx from xx where xxx for update

自旋锁与非自旋锁

自旋锁其实就是前一篇中说的轻量级锁,还有兄弟是自适应自旋锁,目前自旋锁是被废了的太子,自适应自旋锁顶替了太子之位了,因为它可以自动的动态调整自旋次数,以达到最高效的运行状态,具体根据那些参数自动调整。而非自旋锁则是当目标资源被占用时,直接进入休眠状态了(遇到困难,睡大觉),等资源就绪后会被再次唤醒并尝试获取锁,这样就造成了反复的内核态与用户态的切换,浪费系统资源。一张图,展示一下自旋与非自旋的差异性:

画图是真的费眼睛,大家有什么比较好的画图方式吗?可以分享一下。这里再解释一下,自旋锁并不完美,有很多缺点,比如自旋时如果此时控制不当,会造成CPU资源的浪费,JDK也在不断的优化这些锁的性能。

再往深了说,其实在自旋锁中还分为三种:TicketLock、CLHlock和MCSlock。

TicketLock

看名字就知道:票据锁,即想要获取锁,你要出示对应的凭证,对上号了,才能把锁给你。跟你去银行取钱似的,拿对卡,输对密码才能给你取钱。

/*** FileName: TicketLock* Author:   RollerRunning* Date:     2020/12/3 9:34 PM* Description:*/
public class TicketLock {//保证可见性volatile int flag = 0;AtomicInteger ticket = new AtomicInteger(0);void lock() {int getTicket = ticket.getAndIncrement();while (getTicket != flag) {}}void unlock() {flag++;}
}

还记得前面讲的Volatile是基于总线监听实现的可见性吗?这里如果线程特别多,大家都在监听flag,这对于带宽容量有限的主存来说,线程的不断增加,压力会越来越大,这也桑畅TicketLock的缺点。

CLHlock

CLHLock其实是三个人发明的:Craig, Landin和Hagersten所以叫CLH了,它的底层是基于链表的公平自旋锁。赫赫有名的AQS(AbstractQueuedSynchronizer)就是基于这种锁变种而来的。在CLH中,所有相互竞争的线程都被放到一个链表中排队,每一个线程被抽象成一个链表的节点,每一个节点在前趋结点的locked域上自旋等待。当前驱释放锁状态,则后续节点就可以进行获取锁的操作。在以后的文章里会手撕AQS的,今天主要介绍一下有啥,埋个种子。

MCSlock

CLHLock这么牛13了,还整个MCSLock干啥呢?原因竟然是为了兼容硬件系统,从架构上来看,分为三大怪物:SMP, MPP和NUMA,问题就出在了这个NUMA上了。它的中文名是:非一致存储访问结构。正是因为这种结构,导致了在使用CLHLock时,后节点在获取前节点中的locked域状态时内存过远。行了,当做八股文背住就行了,面试估计也没人问这个,写BUG更用不到。

公平锁与非公平锁

公平锁

是指多个线程按照申请锁的顺序来依次获取锁,线程直接进入队列中排队,当共享资源可用时,只有队列中的第一个线程才能获得锁。公平锁的优点是等待锁的线程不会饿死。但是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大。

非公平锁

是指多个线程加锁时直接尝试获取锁,加锁失败时,才会被放入队列中去。但如果此时锁刚好可用,那么这个线程可以直接获取到锁,所以非公平锁有可能出现后申请锁的线程先获取锁的场景。优点是可以减少唤起线程的开销,吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程。缺点是处于等待队列中的线程可能会饿死,或者等很久才会获得锁。

重入锁和不可重入锁

可重入锁

以Java为例ReentrantLock和Synchronized都是可重入锁,是指在同一个线程在外层方法获取锁的时候,如果其内部调用的方法也有锁,则可以直接获取锁,不会因为之前的锁还没释放而阻塞,一定程度上避免了死锁。在下面的代码中,testRoller()和testRunning() 都是加了锁的两个方法,因为Synchronized是可重入锁,所以在testRoller()中调用testRunning()时,可以直接获取锁。

/*** FileName: TestLock* Author:   RollerRunning* Date:     2020/12/3 9:34 PM* Description:*/
public class TestLock {public synchronized void testRoller() {System.out.println("testRoller....");testRunning();}public synchronized void testRunning() {System.out.println("testRunning....");}
}

共享锁和独占锁

独享锁

是一种吃独食的锁,一次只能被一个线程持有。如果线程A对共享数据独占锁以后,那么其他线程就都没有机会再加锁了,获得排它锁的线程就拥有了对该数据的读写权限。JDK中的Synchronized以及Lock的实现类就是互斥锁。

共享锁

是指这类锁可被多个线程持有。如果线程A对共享数据加上共享锁后,则其他线程也只能对共享数据加共享锁,不能加排它锁。而获得共享锁的线程只能读数据,不能修改数据。

最后贴一张锁分类图:

超屌的多线程锁分类,了解一下?相关推荐

  1. 【线程、锁】什么是AQS(锁分类:自旋锁、共享锁、独占锁、读写锁)

    文章目录 1. 什么是AQS 1.1 锁分类 1.2 具体实现 2. AQS底层使用了模板方法模式 3. AQS的简单应用 参考 1. 什么是AQS AQS:全称为AbstractQuenedSync ...

  2. 锁锁锁-多线程锁-多进程锁

    锁是什么 锁在现实生活意义在于通过加锁的方式达到隐私保护或者独占的意义. 锁在程序世界里,加锁是方法,目的在于①独占②同步. 多线程锁–锁的源起 1.为了尽可能压榨CPU资源,神奇的码农们发明了轻量级 ...

  3. Kraken:使用精确比对的超快速宏基因组序列分类软件

    文章目录 Kraken:使用精确比对的超快速宏基因组序列分类 热心肠日报 摘要 主要结果 图1. Kraken序列分类算法 图2. 基于三个模拟宏基因组的分类程序准确性和速度比较 图3. 基于三个模拟 ...

  4. 【JUC并发编程06】多线程锁 (公平锁和非公平锁,死锁,可重锁)

    文章目录 6 多线程锁 (公平锁和非公平锁,死锁,可重锁) 6.1 synchronized 锁的八种情况 6.2 对上述例子的总结 6.3 公平锁和非公平锁 6.4 可重入锁 6.5 死锁 6 多线 ...

  5. java判断线程是否死锁_c++多线程锁 Mutex  自动判断死锁

    c++多线程锁可以使用absl::Mutex  std::mutex这两种,下面是demo代码. 使用absl:Mutex的时候打印: [mutex.cc : 1338] RAW: Cycle: [m ...

  6. linux下java多线程_Linux系统下Java问题排查——cpu使用率过高或多线程锁问题

    原标题:Linux系统下Java问题排查--cpu使用率过高或多线程锁问题 一个系统.特别是多线程并发的后台系统,在某些特定场景下,可能触发系统中的bug:导致cpu一直居高不下.进程hang了或处理 ...

  7. 【JUC】第三章 多线程锁、CallableFuture 接口

    第三章 多线程锁.Callable&Future 接口 文章目录 第三章 多线程锁.Callable&Future 接口 一.多线程锁 1.synchronized 2.公平锁/非公平 ...

  8. CC00155.bigdatajava——|JavaMySQL.高级.V27|——|MySQL.v28|锁分类|

    一.MySQL的锁分类 ### --- mysql锁分类--> MySQL数据库由于其自身架构的特点,存在多种数据存储引擎, --> MySQL中不同的存储引擎支持不同的锁机制. --&g ...

  9. 多线程锁详解之【临界区】

    更多的锁介绍可以先看看这篇文章:多线程锁详解之[序章] 正文: 一般锁的类型可分为两种:用户态锁和内核态锁.用户态锁是指这个锁的不能够跨进程使用.而内核态锁就是指能够跨进程使用的锁.一般书中会说,wi ...

最新文章

  1. 宽度优先遍历(BFS)
  2. 大四学生对于晚睡的思考
  3. jQuery 插件-(初体验一)
  4. Wpf控件ListBox使用实例2
  5. 以xml html编写的的学生管理系统,基于.NET和XML的学生中心数据库管理系统的研究与实现...
  6. Python适合初学者入门
  7. 事情没有想象中那么难--JX官网首页3D粒子效果
  8. 每日一“酷”之string
  9. python public_python中private、protectedamp;public
  10. 6.wireshark使用全解
  11. php 图像居中裁剪函数,PHP 实现的自定义图像居中裁剪函数示例
  12. RHEL 6 LAMP(一)
  13. visio如何粘附或取消粘附连接线
  14. Android如何绘制矩形方框,绘制矩形(方法二、空心的)
  15. sql语句中大于号小于号的处理
  16. VMware10虚拟机请选择要安装OS X的磁盘解决方法
  17. 谷粒商城 - 项目环境搭建
  18. ubuntu teamviewer被检测为商业用途
  19. 2021-08-29
  20. 你做的数据运营,90%都是无用功

热门文章

  1. Swift编程十二(方法)
  2. windows如何安装pycocotools
  3. 解决get传参有数组问题
  4. Python实训笔记
  5. 如何自学前端,这里有5个技巧值得借鉴
  6. 揭秘2023年高新软件技术
  7. 2021计算机系统大作业 CSAPPHello‘s P2P
  8. 八年级上册历史知识点(第2课 第二次鸦片战争)
  9. Pentadactyl使用技巧
  10. springmvc(四) springmvc的数据校验的实现