Java的锁有哪几种?
公平锁vs非公平锁(2020-7-14)
共享锁vs独占锁(排它锁)
乐观锁vs悲观锁
可重用锁
自旋锁
啊啊啊~~~~~~
怎么这么多的锁?它们的出现是解决什么问题的?
公平锁和非公平锁
说到这两个分类的锁,那就要说它们判断公平和非公平的标准是什么?
锁我们都知道,是Java在多线程的情况下,会对某一资源进行竞争,那么竞争就会产生排队,就像咱们春节买车票,大家到火车站都得排着队买(这里就有人要抬杠,现在手机买,根本不用排队,都是靠抢的。说这话的人,我只能说,小伙子你的路还很长啊~)。那么,这个锁的公平标准就出来了。对,就是“排队”!能够通过排队获取到的锁就是公平锁,反之,不通过排队就能获取到的锁就是非公平锁。
好了知道了这两个锁的定义,那么这俩玩意儿在哪儿会用到呢?(面试官问这个真是够叼的。。。)
来带你回忆一下,我们在面试中经常会用到HashMap,啊~怎么又是这玩意儿>_<。HashMap里面有个ConcurrentHashMap,它的实现用到了一个锁,那就是ReentrantLock。对就是这玩意儿,它实现锁的功能时,提供了两种方式。bingo!恭喜你猜对了!就是用到了我们上面讲的那两个玩意儿——公平锁和非公平锁。
那么,这两个锁的实现是基于什么实现的呢?有个专业名词叫AQS,博主知道这个词是在面试的时候才知道的,惭愧啊~~~
这个词的全称叫AbstractQueuedSynchronizer。其实,我们去看源码,发现这个类还继承了一个类——AbstractOwnableSynchronizer(AOS)。
这个父类我们这里就不多说了,我们重点是看AbstractQueuedSynchronizer,这类里面定义了两个实现如下图:
这两个我们看这个类名就知道了,这两个就是实现公平锁和非公平锁的类。
我们再接着看下ReentrantLock的类,我们发现它里面的两个锁都是继承Sync,而Sync继承了AbstractQueuedSynchronizer。
这里我再来讲一讲这两个锁的优缺点,为什么默认是非公平锁呢?
公平锁:能够让每个加lock的线程,按照FIFO的方式,按照顺序获取到锁。不会造成饿死现象。缺点,就是因为要实现队列,需要阻塞和唤醒线程,造成CPU的消耗。
非公平锁:可以让线程快速获取到锁,减少线程的唤醒。(通俗理解的场景就是,当一个锁释放了,我刚进来一个线程,哇!有锁了,直接给我加上呗。对!就是这么霸道,来得早不如来得巧~~~如果没有,就直接随机获取咯~)。缺点,因为是随机性,有的线程可能等到死都等不到锁。点背,喝凉水都塞牙~~~
我们来具体看看这两个锁的实现:
公平锁FairSync:
上图我做了标记,hasQueuedPredecessors() 这个方法去做队列查询,查询阻塞队列中是否有等待线程,如果没有才会调用CAS去修改状态同步变量,进而实现同步锁。
非公平锁NonfairSync:
上图中在获取锁的时候,如果同步锁状态为0,直接进行CAS修改状态同步变量,根本没有关心阻塞队列中是否有线程等待。
ps: 这里说下ReentrantLock默认用非公平锁,可能是因为它有更好的性能,满足高吞吐量,毕竟饿死的情况是比较少的嘛!
来这个类里面发现了一个有趣的东西(什么东西呢?提示下:内部类Node),如下图:
这个内部类是用来干什么的?上图的几个参数单独拿出来是什么意思?我们下章见~~~