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

年轻人,醒醒吧!此时不搏何时搏!本文主要讲一下常见的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++;    }}


推荐阅读