常见的锁策略

前言

在多线程编程中,锁的策略是极其重要的一个概念。因为锁往往是和线程安全相挂钩的,所以在这里了解锁的策略是非常有必要的。

 一.乐观锁和悲观锁

简单介绍下概念:

1.乐观锁

乐观锁就是假设数据一般情况下不会发生冲突,所以在数据真正更新的时候,才会对数据是否真正产生并发冲突进行检测,如果发生冲突了,则返回用户错误信息,让用户决定如何去做。

举个例子:加入现在有一个同学想要找老师问问题

该同学认为 "老师是比较闲的, 我来问问题, 老师大概率是有空解答的". 因此同学直接就来找老师.(没加锁, 直接访问资源) 如果老师确实比较闲, 那么直接问题就解决了. 如果老师这会确实很忙(也就是检测到了冲突), 那么同学 B也不会打扰老师, 就下次再来(虽然没加锁, 但是能识别出数据访问冲突).   这个是乐观锁.

2.悲观锁

悲观锁总是假设最坏的情况,所以在每次上锁的时候都认为别人会修改,所以每次去拿数据的时候都会上锁。

举例:还是有个同学想要问问题

该同学认为,老师一定是在忙着的。不管是在上课还是在写教案,该同学都是认为该老师是一直处于忙着的状态。所以该同学就会跟老师给约定个时间来问问题(就相当于加锁操作),在得到肯定回复之后,才会真正的去问问题。如果得到了否定的回答,那就再等一段时间,下次再和老师确定时间。

二.读写锁

多线程之间,数据的读取方之间不会产生线程安全问题,但 数据的写入方互相之间以及和读者之间都需要进行互斥 。如果两种场景下都用同一个锁,就会产生极大的性能损耗。所以读写锁因此而产生。
读写锁( readers-writer lock ),看英文可以顾名思义,在执行加锁操作时需要额外表明读写意图,复数读者之间并不互斥,而写者则要求与任何人互斥。
在多线程环境下,一个线程对于数据的访问主要有两种形式:读数据和写数据
此时就分为三种情况:
  • 两个线程都只是读一个数据, 此时并没有线程安全问题. 直接并发的读取即可.
  • 两个线程都要写一个数据, 有线程安全问题.
  • 一个线程读另外一个线程写, 也有线程安全问题.

读写锁特别适合于 "频繁读, 不频繁写" 的场景中. (这样的场景其实也是非常广泛存在的).也就是读的多,写的少。

三.自旋锁

一般来说,线程在竞争锁的时候失败后,需要放弃CPU,等待之后的调度(这个过程可能有很长),但是其实大部分情况下,虽然当前线程强锁失败了,但是过不了多久,这个CPU就空闲了。此时就没必要放弃CPU,也就是可以一直来竞争锁。这中锁的竞争策略就是自旋锁。

举个例子,假如你在追你的女神,此时你的女神已经和别人在一起了。但是他们俩有可能很快就会分手了,此时你就没必要放弃去追女神(CPU),你每天和她发信息,不断重复尝试,假如她分手了,此时你就能立刻得到。 大概就是这个意思。

挂起锁和自旋锁

挂起等待锁 :去准女神的时候,得知她已经有了男朋友(有锁),此时你就陷入沉沦不能自拔.... 过了很久很久之后, 突然女神发来消息, "咱俩要不试试?" (注意,这个很长的时间间隔里, 女神可能已经换了好几个男票了).
自旋锁: 死皮赖脸坚韧不拔. 仍然每天持续的和女神说早安晚安. 一旦女神和上一任分手, 那么就能
立刻抓住机会上位.

四.公平锁和非公平锁、

假设现在有三个线程A B C,A此时先尝试获取到锁成功,然后B尝试获取到锁失败,阻塞等待。此时C也尝试获取到锁失败,阻塞等待。那么此时如果A释放了锁,那么会发生什么?

1.公平锁

遵循先来后到的原则:B比C先来的,当A释放锁之后,B会获取到锁。

2.非公平锁

B和C谁先获取到锁不一定。B 和 C都有先于对方一步可能获取到锁。

注意:synchronized 是非公平锁

操作系统内部的线程调度就可以视为是随机的 . 如果不做任何额外的限制 , 锁就是非公平锁 . 如果要
想实现公平锁 , 就需要依赖 额外的数据结构 , 来记录线程们的先后顺序 .
公平锁和非公平锁 没有好坏之分, 关键还是看适用场景.

五.可重入锁和不可重入锁

1.可重入锁

可重入锁的字面意思是 可以重新进入的锁 ,即 允许同一个线程多次获取同一把锁。
比如在一个线程的代码块中有个操作时递归操作,那么在递归的过程中这个锁会阻塞自己吗?答案是如果时不会,那么这个锁就是可重入锁。( 所以可重入锁也叫做递归锁)。
Java 里只要以 Reentrant 开头命名的锁都是可重入锁,而且 JDK 提供的所有现成的 Lock 实现类,包括 synchronized 关键字锁都是可重入的。
Linux 系统提供的 mutex 是不可重入锁 .

2.不可重入锁

假如还是刚才的递归例子,如果锁时不可重入的。那么在同一个线程中,在函数递归的过程中,执行到第二次的时候需要加锁,但是这个锁是当前线程在使用并且来进行递归了。那么就会一直等待自己的线程释放自己的锁。那么就很扯淡,自己不想释放锁,自己还得等待自己释放。这种情况就是不可重入锁。


总结:锁策略是需要重点学习和理解的,它适用于任意的多线程编程语言。

                             

你可能感兴趣的:(java,开发语言)