怎么看网站是用什么程序做的/网络营销推广专家
一、ReentrantLock
简介
ReentrantLock
是 Java 并发包 java.util.concurrent.locks
中的一个可重入的互斥锁,提供了比内置锁(synchronized
)更灵活的锁操作。它支持公平锁和非公平锁两种模式,并且可以尝试非阻塞地获取锁、尝试获取锁并指定等待时间等操作。
二、公平锁与非公平锁的区别
1. 非公平锁(NonfairSync)
非公平锁允许插队,可能会导致某些线程长期等待。非公平锁的 lock()
方法首先尝试通过 CAS 直接获取锁,如果失败则进入 AQS 的 acquire
方法。
final void lock() {if (compareAndSetState(0, 1)) { // 尝试通过 CAS 获取锁setExclusiveOwnerThread(Thread.currentThread());} else {acquire(1); // 如果 CAS 失败,进入 AQS 的 acquire 方法}
}
2. 公平锁(FairSync)
公平锁按照 FIFO 顺序获取锁,保证线程的公平性。公平锁的 lock()
方法直接调用 AQS 的 acquire
方法,不会尝试直接获取锁。
final void lock() {acquire(1); // 直接进入 AQS 的 acquire 方法
}
三、hasQueuedPredecessors
方法解析
hasQueuedPredecessors
方法是公平锁实现的核心,用于检查队列中是否有等待执行的线程。如果队列中有等待线程,则当前线程不能直接获取锁,必须排队等待。
public final boolean hasQueuedPredecessors() {Node t = tail; // 获取尾节点Node h = head; // 获取头节点Node s;return h != t && // 如果头尾不一致,说明队列中有节点((s = h.next) == null || s.thread != Thread.currentThread()); // 检查头节点的下一个节点是否为当前线程
}
-
h != t
:判断队列是否为空。如果头尾节点不一致,说明队列中有等待线程。 -
s.thread != Thread.currentThread()
:检查头节点的下一个节点是否为当前线程。如果不是,说明有其他线程在队列中等待。
四、公平锁与非公平锁的实现细节
1. 公平锁的 tryAcquire
方法
公平锁在尝试获取锁之前,会先调用 hasQueuedPredecessors
方法检查队列中是否有等待线程。
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;
}
2. 非公平锁的 tryAcquire
方法
非公平锁直接尝试通过 CAS 获取锁,不会检查队列。
protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0 && compareAndSetState(0, acquires)) { // 直接尝试获取锁setExclusiveOwnerThread(current);return true;}return false;
}
五、lockInterruptibly
方法
lockInterruptibly
方法尝试获取锁,但如果当前线程被中断,则抛出 InterruptedException
。
public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);
}
-
acquireInterruptibly
:AQS 的方法,尝试获取锁。如果线程被中断,则抛出InterruptedException
。
使用场景
适用于需要响应中断的场景,例如在任务被取消时能够及时释放资源。
示例代码
ReentrantLock lock = new ReentrantLock();
try {lock.lockInterruptibly();// 执行需要同步的代码
} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 处理中断
} finally {lock.unlock(); // 确保释放锁
}
六、tryLock
方法
tryLock
方法尝试获取锁,但不会阻塞。如果锁立即可用,则返回 true
,否则返回 false
。
public boolean tryLock() {return sync.nonfairTryAcquire(1);
}
-
nonfairTryAcquire
:尝试获取锁,不会检查队列。
使用场景
适用于需要尝试获取锁但不希望阻塞的场景,例如在高并发环境下避免线程长时间等待。
示例代码
ReentrantLock lock = new ReentrantLock();
if (lock.tryLock()) {try {// 执行需要同步的代码} finally {lock.unlock(); // 确保释放锁}
} else {// 锁不可用,执行其他逻辑
}
tryLock
方法还有一个重载版本,允许指定等待时间:
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
-
tryAcquireNanos
:尝试在指定时间内获取锁。如果在指定时间内获取到锁,则返回true
,否则返回false
。
示例代码
ReentrantLock lock = new ReentrantLock();
try {if (lock.tryLock(1, TimeUnit.SECONDS)) {try {// 执行需要同步的代码} finally {lock.unlock(); // 确保释放锁}} else {// 锁不可用,执行其他逻辑}
} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 处理中断
}
七、unlock
方法
unlock
方法释放锁。如果当前线程不是锁的持有者,则抛出 IllegalMonitorStateException
。
public void unlock() {sync.release(1);
}
-
release
:AQS 的方法,尝试释放锁。
使用场景
在使用 lock
、lockInterruptibly
或 tryLock
方法获取锁后,必须在 finally
块中调用 unlock
方法,以确保锁被正确释放,避免死锁。
示例代码
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {// 执行需要同步的代码
} finally {lock.unlock(); // 确保释放锁
}
八、总结
-
非公平锁:先尝试通过 CAS 抢锁,抢不到再排队。这种方式可能会导致某些线程长期等待,但吞吐量较高。
-
公平锁:不会尝试直接获取锁,而是先检查队列中是否有等待线程,只有队首线程才允许尝试获取锁。这种方式保证了线程的公平性,但可能会导致吞吐量较低。
在实际使用中,可以根据具体需求选择公平锁或非公平锁。如果对响应时间要求较高,建议使用非公平锁;如果需要保证线程的公平性,建议使用公平锁。
ReentrantLock
提供了多种方法来控制锁的行为,包括尝试获取锁、尝试获取锁并指定超时时间、检查锁的状态等。这些方法的实现都基于 AQS 的核心机制,通过 tryAcquire
、tryRelease
、acquire
和 release
等方法来实现锁的获取和释放。