腾讯面试题 你了解ReentrantLock吗?

Vala ·
更新时间:2024-09-21
· 813 次阅读

腾讯面试题 你了解ReentrantLock吗? ReetrantLock是一个可重入的独占锁,主要有两个特性,一个是支持公平锁和非公平锁,一个是可重入。

ReetrantLock实现依赖于AQS(AbstractQueuedSynchronizer)(不懂得话可以看我上篇博客)。

ReetrantLock主要依靠AQS维护一个阻塞队列,多个线程对加锁时,失败则会进入阻塞队列。等待唤醒,重新尝试加锁。下图是其类图

支持公平锁和非公平锁

公平锁:多个线程申请获取同一资源时,必须按照申请顺序,依次获取资源。

非公平锁:资源释放时,任何线程都有机会获得资源,而不管其申请顺序。

具体原理分析

在new ReetrantLock对象的时候,可以指定其支持公平锁还是非公平锁

public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 公平锁原理

FairSync继承Sync,而Sync继承AbstractQueuedSynchronizer。ReentrantLock调用lock方法,最终会调用sync的tryAcquire函数,获取资源。FairSync的tryAcquire函数,**当前线程只有在队列为空或者时队首节点的时候,才能获取资源,否则会被加入到阻塞队列中。**下面的hasQueuedPredecessors函数用于判断队列是否为空,或者当前元素是否为队首元素。

static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } 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; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }

hasQueuedPredecessors函数,如果h==t(队列为空),或者h!=t && s.thread == Thread.currentThread()(队首节点),则返回false,否则返回true

public final boolean hasQueuedPredecessors() { Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); } 非公平锁原理

NoFairSync同样继承Sync,ReentrantLock调用lock方法,最终会调用sync的tryAcquire函数,获取资源。而NoFairSync的tryAcquire函数,会调用父类Sync的方法nofairTryAcquire函数。通过对比可以发现,如果资源释放时,新的线程会尝试CAS操作获取资源,而不管阻塞队列中时候有先于其申请的线程。

static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } 可重入性

可重入性:获取独占资源的线程,可以重复得获取该独占资源,不需要重复请求。

请求独占资源时,可重入性的体现

ReentrantLock在申请资源的时候,都会判断当前持有独占资源的线程是不是当前线程,如果是的话,只是简单得将state值加1.记录当前线程的重入次数。

else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } 释放资源时,可重入性的体现

ReentrantLock在释放资源的时候,都会调用tryRelease,只有state值为0的时候,才会释放资源。换句话说就是,重入多少次,就必须释放多少次

protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
作者:zycxnanwang



腾讯面试 面试题 腾讯面试题 面试 reentrantlock 腾讯

需要 登录 后方可回复, 如果你还没有账号请 注册新账号