理解这些常见锁策略,恭喜你,又进阶了~

目录

一、常见锁策略

1.1乐观锁 and 悲观锁 

1.2读写锁 and 普通互斥锁

1.3重量级锁 and 轻量级锁

1.4自旋锁 and 挂起等待锁

1.5公平锁 and 非公平锁

1.6可重入锁 and 不可重入锁

1.7信号量Semaphore

二、相关经典面试题

2.1你是怎么理解乐观锁和悲观锁的,具体怎么实现呢?

2.2介绍一下读写锁?

2.3什么是自旋锁,为什么要使用自旋锁策略呢?

2.4synchronized 是可重入锁么?


一、常见锁策略

1.1乐观锁 and 悲观锁 

乐观锁:

       总是假设数据大多数情况下不会发生冲突,只有当数据正式提交更新的时候,若发现了数据冲突,才会进行将冲突的信息反馈给用户,让用户决定如何去解决;总的来说:乐观就是指锁冲突的概率不高,因此做的工作就可以简单一些,因此性能也比较高,但往往不能处理到所有问题,需要一定的系统复杂度

        举个栗子(乐观):临近考试,我就比较淡定,对于有些题我就觉得,上课既然讲的也很少,因该也就很少考,所以就不去复习这些题了;另一方面,我对自己平时的学习很自信,因此,也就没有必要去复习了;

使用场景:适用于读操作多,写操作少的场景.

悲观锁:

        总是假设最坏的情况,觉得数据大多数情况下都会发生冲突,所以再每次拿数据的时候都会给数据上锁,这样就算别人想拿到这个数据,也需要阻塞等待直到解锁;总的来说:预测锁冲突的概率较高,因此做的工作就要复杂一些,因此性能不高,但比较安全,synchronized就是一个典型的悲观锁;

        举个栗子(悲观):临近考试,我就比较慌,总感觉什么题都会考到,让我辗转难眠,于是我就多去复习,什么都想复习一下,总担心哪里复习不到;

使用场景:适用于读操作少,写操作多的场景.

1.2读写锁 and 普通互斥锁

读写锁:

        咱们清楚的一点是:读锁于读锁之间,不会产生竞争,写锁和写锁以及读锁和写锁之间,存在竞争,因此,读写锁分为两种,一种是写锁(只能有一个线程拥有),一种是读锁(可以有多个线程拥有),这是为了在执行读写操作的时候要额外表明读写意图,读者与读者之间并不互斥,而写者要求于任何人互斥;

        于是读写锁相比于普通互斥锁就少了很多竞争;那什么是普通互斥锁呢?往下看!

普通互斥所:

        这就不同于读写锁有那么多要求,普通互斥锁就如同synchronized的一样,当两个线程竞争同一把锁,就会阻塞等待;

1.3重量级锁 and 轻量级锁

重量级锁:

        加锁的开销比较大,为什么呢?,大量的内核态用户态的切换,很容易引发线程调度,因此开销是比较大的;

轻量级锁:

        加锁机制尽可能不通过系统调度来进行用户态和内核态的切换,而是尽量在用户态完成,实在不行,再切换;也就是说,可以说是典型的纯用户态的加锁逻辑,开销较小;

思考:synchronized刚开始是一个轻量级锁,但是一旦所冲突比较严重时,就变成了重量级锁;

1.4自旋锁 and 挂起等待锁

自旋锁:

        是一个典型的轻量级锁,可以简单的理解为“忙等”,消耗大量的CPU反复询问当前锁是否就绪;(只要拿不到锁,就死等);缺点很明显,就是消耗大量的CPU资源,做无用功;优点是可以第一时间拿到锁;

挂起等待锁:

        是一个典型的重量极锁,可以简单的理解为“不忙等”,进入等待状态时,让出CPU资源,使其可以进行一些其他有意义的事情;优点很明显,合理利用CPU资源提高效率;缺点就是可能不那么及时拿到锁;

        举个栗子:想象这样一个场景,挂起等待锁和自旋锁同时追一个妹子;

        挂起等待锁:这个妹子给他说,这阵子我有点忙,过一段时间咱们再一起去看电影;挂起等待锁就很好的利用在这段时间里努力学习提升自己,然后当妹子给他打电话的时候,他还在废寝忘食的学,以至于忘记接电话,那这个妹子可能就要跑路喽~

        自旋锁:整天死皮赖脸的给这个妹子发消息,早安晚安...经常打电话,这个女生就像被监视了一样,无论发生什么情况,这个自旋锁肯定能第一时间知道;

1.5公平锁 and 非公平锁

公平锁:

        锁获得的顺序与线程方法先后顺序保持一致,优点便是执行的时候,顺序是可知的;

非公平锁:

        锁的获取顺序与线程方法的先后顺序无关,优点就是性能很高;

        注意:再没有任何额外限制的时候,锁就是非公平的,若要公平,需要额外提供数据结构,记录线程的先后顺序,公平锁与非公平锁之间也没有好坏之分,具体看应用场景;synchronized就是非公平锁;

1.6可重入锁 and 不可重入锁

        简单来说:

        可重入锁:同一个线程针对同一把锁,连续加锁两次,不会死锁;

        不可重入锁:同一个线程针对同一把锁,连续加锁两次,会死锁;

这个之前博主专门写过文章,可以来看看~

http://t.csdn.cn/PjjkZ

1.7信号量Semaphore

        信号量本身是一个计数器,表示可用资源数,当计数器器为0的时候,再申请资源,就会产生阻塞,直到有线程释放资源为止;另外,锁可以视为一个特殊的信号量(可用资源数为1),信号量可以视为一个更广义的锁;

        基本操作有两个:(注意:P,V为荷兰语缩写)

        P操作(acquire() 方法):申请一个资源,可用资源 - 1;

        V操作(release() 方法):释放一个资源,可用资源 + 1;

Semaphore semaphore = new Semaphore(//初始资源数(阈值))

二、相关经典面试题

2.1你是怎么理解乐观锁和悲观锁的,具体怎么实现呢?

        怎么理解乐观锁和悲观锁,上面的概念已经很清楚了,这里提一下具体实现是什么:

        悲观锁的实现就是先加锁(比如借助操作系统提供的 mutex), 获取到锁再操作数据. 获取不到锁就 等待.

        乐观锁的实现可以引入一个版本号. 借助版本号识别出当前的数据访问是否冲突. 

2.2介绍一下读写锁?

        具体介绍,上面已经很清楚了,忘了的话,记得往上翻翻;

2.3什么是自旋锁,为什么要使用自旋锁策略呢?

        问道为什么要使用自旋策略,实际上就是再问你,自旋锁的优点是什么,最好把缺点也说一下,面试官一天要面试很多人,而面试官实际上和你是站在一方的,就是一个让你展现自我的地方,所以,面试官自然是希望多说点你了解的

2.4synchronized 是可重入锁么?

        是可重入锁,是通过一个计数器来记录持有锁的线程的身份,发现锁是当前锁,计数器直接++;具体可以看一下博主整理出的这篇文章:http://t.csdn.cn/93QQI

上面这条链接详细的介绍了可重入锁的底层逻辑


理解这些常见锁策略,恭喜你,又进阶了~_第1张图片

你可能感兴趣的:(操作系统,java,线程安全,锁策略)