并发场景下如何保证线程安全,首先就会想到Synchronized,是的没错但是今天我们来看一下和syn锁具有相同作用而且扩展性更高的锁ReentrantLock(重入锁),以下分析基于JDK1.8,我们先来看看注释。
一看作者,哇塞Doug Lea大神,一种崇拜油然而生,我们来看看大神在类的注释上写了些什么
可重入锁具有和Sync锁相同的方法行为,但是更有扩展能力。
调用lock方法时,当没有其他线程获取锁的时候,当前线程能成功获取锁,如果一个线程拥有锁,这个方法将立即返回,可使用上述两个方法检查。
构造方法中包含一个可选的参数,如果是true则创建一个公平锁,锁倾向于等待时间最长的线程,如果为false则不能保证任何的访问顺序,使用公平锁吞吐量较低,很少有饥饿。
建议的使用方式。
此外实现Lock接口可以检查锁的状态,一个线程最多可重复获取21亿次锁,还有序列化和反序列化相关的介绍。
接下来我们看类行为、属性:
其中包含三个核心内部类Sync类继承了AQS,NonfairSync类、FairSync类分别继承了Sync类,方法中比较核心的是lock、trylock、unlock方法。
重入:
重入是指当前线程获取锁后,能重复获取锁,看源码前思考下,要是让你去实现,你怎么做?当一个线程获取锁后 ,再次获取时先看这个线程与锁持有线程是否为同一个,如果是获取成功,既然可以重复获取锁,肯定要记录下获取锁的次数,每次获取增加一个,释放锁减少一个,当变量为0时表示锁可以被其他线程获取。
非公平锁:
非FIFO,不考虑顺序的获取,就是非公平锁,默认非公平锁。
获取锁方法,先判断state是否为0,满足条件则说明当前锁可获取,然后通过cas将0修改1,并把当前线程设置为锁持有线程,返回成功;相同线程下次再进入的时候state肯定不是0,进入下一个判断如果当前线程是锁持有线程则state+1,获取成功,否则返回false;
释放锁方法,判断调用释放方法的线程如果不是锁持有线程抛出异常,否则判断state是否为0,如果为0,则将锁持有线程设置为null,设置state为0,返回成功,否则返回失败,如果一个线程多次获取锁,再释放锁的时候,只会state-1,然后返回false;
公平锁:
FIFO,等待时间最长的请求获取锁,也可以说是按顺序的获取,这就是公平锁。
获取锁方法,与非公平锁的不同在于多判断了同步队列中当前节点是否有前驱节点,返回true说明队列中存在锁请求,需要排队获取锁。
举个例子:
公平锁:当前A持有锁,队列中BCDE,这时线程F想要获取锁,直接加到队尾等待。
非公平锁:当前A持有锁,队列中BCDE,这时线程F想要获取锁,当A释放锁的时候唤醒队列中的B线程,这时B、F具有相同的优先级,同时cas竞争。