答:第一步:平常没用过,synchronized无法满足所有的场合。ReentrantLock是java类实现的,而synchronized是jvm层实现的。ReentrantLock则更灵活但是也很危险。它可以中断正在请求锁的线程,也可以让线程无限请求等待。
第二步:然后我简单说下它的原理。
(1)它的原理是基于AQS(AbstractQueuedSynchronizer ),多个线程抢锁时,如果争抢失败则会进入阻塞队列。等待唤醒,重新尝试加锁。
(2) AQS的子类有公平锁FairSync和非公平锁NofairSync,ReentrantLock的无参构造默认是非公平锁,有参构造参数是true可以设置成公平锁。
(3)公平锁:ReentrantLock调用lock方法,最终会调用Sync类的tryAcquire函数,获取资源。当前线程只有在队列为空或者时队首节点的时候,才能获取资源,否则会被加入到阻塞队列中。
(4)非公平锁:也是调用sync的tryAcquire,不同的是tryAcquire还会调用nofairTryAcquire。
(5)公平锁和非公平锁的区别:非公平锁在调用NonfairSync的lock的时候就会马上进行CAS抢锁,抢不到就和公平锁一样进入tryAcquire方法尝试抢锁,如果发现锁被释放了(state==0),非公平锁马上CAS抢锁,而不会管阻塞队列里是否有线程等待。而公平锁会排队等待。
ReentrantLock:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平锁:FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
//尝试直接获取锁,返回值是boolean,代表是否获取到锁
//返回true:
//1.没有线程在等待锁;
//2.重入锁,线程本来就持有锁,也就可以理所当然可以直接获取
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//关键点
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//判断是有锁的线程是否为当前线程。
//可重入就在这里体现的,同一个线程多次调用lock方法
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
非公平锁:NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//不管AQS队列有没有等待的线程,直接开始抢
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
记忆力障碍患者的回答(我就是):
第一步:平常没用过,synchronized用的比较多,但是不能满足所有要求。ReentrantLock更加灵活,它可以中断正在请求锁的线程等操作。
第二步:简单说下原理,内部有两种实现,一种是公平锁,一种是非公平锁,默认是非公平锁,它有个tryAcquire方法来尝试获取锁。
然后呢,非公平锁会尝试两次抢锁,第一次抢不到会等待锁释放,一释放马上再CAS抢锁,再抢失败就进入阻塞队列等待唤醒。
回答思路:1.说明ReentrantLock是Java类,它与synchronized相比能多做什么事情; 2.说下内部的公平锁和非公平锁,重点说下非公平锁有两次“霸道”抢锁。
欢迎一起交流学习!
参考资料:https://blog.csdn.net/zycxnanwang/article/details/105321401