前言
在多线程编程中,锁的策略是极其重要的一个概念。因为锁往往是和线程安全相挂钩的,所以在这里了解锁的策略是非常有必要的。
简单介绍下概念:
乐观锁就是假设数据一般情况下不会发生冲突,所以在数据真正更新的时候,才会对数据是否真正产生并发冲突进行检测,如果发生冲突了,则返回用户错误信息,让用户决定如何去做。
举个例子:加入现在有一个同学想要找老师问问题
该同学认为 "老师是比较闲的, 我来问问题, 老师大概率是有空解答的". 因此同学直接就来找老师.(没加锁, 直接访问资源) 如果老师确实比较闲, 那么直接问题就解决了. 如果老师这会确实很忙(也就是检测到了冲突), 那么同学 B也不会打扰老师, 就下次再来(虽然没加锁, 但是能识别出数据访问冲突). 这个是乐观锁.
悲观锁总是假设最坏的情况,所以在每次上锁的时候都认为别人会修改,所以每次去拿数据的时候都会上锁。
举例:还是有个同学想要问问题
该同学认为,老师一定是在忙着的。不管是在上课还是在写教案,该同学都是认为该老师是一直处于忙着的状态。所以该同学就会跟老师给约定个时间来问问题(就相当于加锁操作),在得到肯定回复之后,才会真正的去问问题。如果得到了否定的回答,那就再等一段时间,下次再和老师确定时间。
读写锁特别适合于 "频繁读, 不频繁写" 的场景中. (这样的场景其实也是非常广泛存在的).也就是读的多,写的少。
一般来说,线程在竞争锁的时候失败后,需要放弃CPU,等待之后的调度(这个过程可能有很长),但是其实大部分情况下,虽然当前线程强锁失败了,但是过不了多久,这个CPU就空闲了。此时就没必要放弃CPU,也就是可以一直来竞争锁。这中锁的竞争策略就是自旋锁。
举个例子,假如你在追你的女神,此时你的女神已经和别人在一起了。但是他们俩有可能很快就会分手了,此时你就没必要放弃去追女神(CPU),你每天和她发信息,不断重复尝试,假如她分手了,此时你就能立刻得到。 大概就是这个意思。
挂起锁和自旋锁
假设现在有三个线程A B C,A此时先尝试获取到锁成功,然后B尝试获取到锁失败,阻塞等待。此时C也尝试获取到锁失败,阻塞等待。那么此时如果A释放了锁,那么会发生什么?
遵循先来后到的原则:B比C先来的,当A释放锁之后,B会获取到锁。
B和C谁先获取到锁不一定。B 和 C都有先于对方一步可能获取到锁。
注意:synchronized 是非公平锁
假如还是刚才的递归例子,如果锁时不可重入的。那么在同一个线程中,在函数递归的过程中,执行到第二次的时候需要加锁,但是这个锁是当前线程在使用并且来进行递归了。那么就会一直等待自己的线程释放自己的锁。那么就很扯淡,自己不想释放锁,自己还得等待自己释放。这种情况就是不可重入锁。
总结:锁策略是需要重点学习和理解的,它适用于任意的多线程编程语言。