您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

超屌的多线程锁分类,你确定不看看吗?

时间:2020-12-08 15:49:46  来源:  作者:

年轻人,醒醒吧!此时不搏何时搏!本文主要讲一下常见的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对共享数据加上共享锁后,则其他线程也只能对共享数据加共享锁,不能加排它锁。而获得共享锁的线程只能读数据,不能修改数据。

最后贴一张锁分类图:

超屌的多线程锁分类,你确定不看看吗?


Tags:多线程锁   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
年轻人,醒醒吧!此时不搏何时搏!本文主要讲一下常见的CAS理论。再者就是说一下锁的分类,什么乐观锁啊,悲观锁、重入锁等等。这篇文章要一网打尽,都介绍一下。把CAS按在地上摩擦中文...【详细内容】
2020-12-08  Tags: 多线程锁  点击:(100)  评论:(0)  加入收藏
更多内容,欢迎关注微信公众号:全菜工程师小辉~Lock接口的实现类ReentrantLock是实现了Lock接口的类,属于独享锁,独享锁在同一时刻仅有一个线程可以进行访问。Lock接口很简单,实现...【详细内容】
2019-08-29  Tags: 多线程锁  点击:(164)  评论:(0)  加入收藏
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的  程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条