读码农翻身之编程世界的那把锁

1、为什么需要锁?
比如有两个线程,分别记为A和B,有一个变量记为X=100,假如线程A先获取到X的值,然后将X加2,而在A没有将102复制到X之前,线程B将X减少了5变成了95,那么此时线程A就会将X的值从95重新修改为102。很显然,这样是有问题的!

2、锁的诞生
任何线程,如果想要操作一个共享变量,需要先申请一把锁,拿到这把锁才能去读取X的值,修改X的值,把X的值重新写回内存。最后再释放锁。这就是锁的定义,那么锁是如何实现的呢?其实底层就是有一个类似boolean的变量,如:boolean lock=false,那么所有线程都去修改lock的值,如果谁先将lock的值修改为true,就认为这个线程抢到了锁。(一定不要认为这个锁是现实世界的锁!)而这种通过无限循环抢锁的特点,命名为“自旋锁”!

3、加锁是一个原子操作
假如有两个线程,都读到了lock==false,都把lock改成true,那这个锁算谁的?其实,我刚开始也是这种想法,然而,其实这个检测lock是否为false以及设置lock为true的操作被合并为了一个不可分割的原子操作。

4、自旋锁存在的问题
上面说的最简单的这种锁称之为自旋锁,但是自旋锁其实有些不足,在递归函数中如果要获取自旋锁时,
读码农翻身之编程世界的那把锁_第1张图片
第一次调用doSomething,获取了自旋锁,然而在这个函数内,又调用了自己,那么第二次调用doSomething方法时,也需要去获取自旋锁,但是这个锁已经在第一次调用的时候持有了,且这个自旋锁还没有释放,那么这个程序就相当于卡住了!形成了死锁。

5、自旋锁的改进
自旋锁存在的问题,简单来说就是“不能重新进入同一个函数,简称不可重入”。改进方案如下:每次成功的申请锁以后,要记录下到底是谁申请的,还要一个计数器记录重入的次数,下一次持有锁的线程再次申请锁只是给计数器加一而已。释放的时候也是一样,把计数器减一,如果等于0了才真正的释放锁。

5、锁的进一步改进
自旋锁的可重入性就这么解决了,但是这么多线程都在拼命的枪也不是办法,空耗CPU也是巨大的浪费啊。
后来又发布了新的锁ReentrantLock,这个锁可以重入,如果抢不到,不要无限循环,在等待队列里待着,直到锁被人释放了再去通知你去抢。
(我们线上的使用方式是如果去抢锁的时候,过了超时时间还抢不到,就返回一个错误码了)

你可能感兴趣的:(读码农翻身之编程世界的那把锁)