概述
ReentrantLock
:一个可重入互斥锁,具有与使用synchronized
方法和语句访问的隐式监视锁相同的基本行为和语义,但具有扩展功能。比如实现公平锁
、超时处理、锁中断。
ReentrantLock由最后一个成功锁定且尚未解锁的线程拥有 。当锁不是由另一个线程拥有时,调用lock的线程将成功获取锁。 如果当前线程已经拥有该锁,该方法将立即返回,加锁次数+1。 当前线程是否持有该锁可以使用方法isHeldByCurrentThread()和getHoldCount()进行检查。
该类的构造函数接受可选的公平参数,当设置true ,即可以使用公平锁。在竞争时,锁有利于授予等待最长时间的线程。 否则,该锁不保证任何特定的访问顺序。 锁竞争比较激烈时,公平锁比非公平锁的整体吞吐量小(通常要慢很多),但是具有更小的时间差异来获得锁定并保证更少的饥饿死锁,饥饿死锁的意思是某一个线程一直获取锁但是一直获取不到,这个在非公平锁的场景是会存在的。 但是请注意,锁的公平性不能保证线程调度的公平性。 因此,使用公平锁的线程可以连续获得多次。
建议的做法是unlock一定要在finally中调用,这样能保证锁的释放,避免死锁,如:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds(阻塞,直到条件成立)
try {
// ... method body
} finally {
lock.unlock()
}
}
}}
除了实现Lock接口,这个类定义了许多public种protected方法用于检查锁的状态。 其中一些方法仅适用于仪器和监控。
此类的序列化与内置锁的操作方式相同:反序列化锁处于未锁定状态,无论其序列化时的状态如何。
此锁最多支持同一个线程的2147483647递归锁。 尝试超过此限制会导致Error从锁定方法中抛出。
我们看下Lock源码:
/**
* Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。
* 它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition 。
*/
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
ReentrantLock使用了NonfairSync
、FairSync
这两个Sync
的子类来实现公平锁与非公平锁,默认是非公平锁。所以我们先看看Sync
、NonfairSync
、FairSync
的实现。
一、Sync源码
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//这个线程第一次加锁,使用CAS设置state=1
if (compareAndSetState(0, acquires)) {
//设置当前锁的当前持有线程,然后退出并返回成功
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
//如果不是第一次加锁,则state向上加acquires(这里默认是1)
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded"); //最大支持2147483647次重入
setState(nextc);
return true;
}
return false;
}
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;
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
* 反序列化,重置为未加锁状态
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
二、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() {
//因为是非公平锁,只要锁没有被占有,那么直接加锁成功,不必调用acquire()方法去阻塞等待。
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//调用acquire()方法去阻塞等待
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
三、FairSync源码
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//直接调用acquire()方法去阻塞等待
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//为保证公平锁,就算当前锁没有被持有,除非自己是第一个,否则不能加锁,因为是tryAcquire尝试加锁,所以不用阻塞,尝试失败就返回
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;
}
}
四、ReentrantLock源码实现
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
// ... 省略 ...
}
static final class NonfairSync extends Sync {
// ... 省略 ...
}
static final class FairSync extends Sync {
// ... 省略 ...
}
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
//如果当前线程不是中断状态则获得锁, 如果当前线程被中断则出现异常。如果是已经在阻塞队列,这个里面会有个循环,不断去获取锁,并判断当前线程是否中断状态,如果是中断状态,则抛出异常,并退出阻塞队列。这样可以在等待一段时间后,及时退出阻塞队列,去做别的事情,避免死锁。
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//tryLock()方法默认调用nonfairTryAcquire(),只要锁可用,即使当前有其他线程正在等待,它也会成功
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public int getHoldCount() {
return sync.getHoldCount();
}
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
public boolean isLocked() {
return sync.isLocked();
}
public final boolean isFair() {
return sync instanceof FairSync;
}
protected Thread getOwner() {
return sync.getOwner();
}
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
public final int getQueueLength() {
return sync.getQueueLength();
}
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}
public String toString() {
Thread o = sync.getOwner();
return super.toString() + ((o == null) ?
"[Unlocked]" :
"[Locked by thread " + o.getName() + "]");
}
}
能看出来,无论公平锁还是非公平锁,lock()方法都是阻塞的,公平与否区别在于当
state
是0时,非公平锁此时调用lock()方法会直接加锁,公平锁会判断自己是不是阻塞队列中最靠前的一个,是则加锁,否则就进入阻塞队列等待。
阻塞可能会导致线程一直等待,造成资源的浪费或者线程死锁,此时我们可以通过trylock()
方法,它调用获取锁失败后是直接返回,不会进入阻塞队列。或者通过lockInterruptibly()
方法,可以随时中断当前线程的阻塞等待状态并退出阻塞队列。
五、Condition类详解
在Java中,Condition类是Java.util.concurrent包下的一个接口,用于支持线程的等待和通知机制。它通常与Lock接口一起使用,用于实现线程间的同步和协调。
Condition类提供了以下方法:
await()
:使当前线程等待,直到被其他线程调用signal()或signalAll()方法唤醒。awaitUninterruptibly()
:类似于await()方法,但是在等待期间不会响应线程中断。await(long time, TimeUnit unit)
:使当前线程等待一段时间,在指定的时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。awaitNanos(long nanosTimeout)
:使当前线程等待一段纳秒时间,在指定的时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。awaitUntil(Date deadline)
:使当前线程等待直到某个时间,如果在指定时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。signal()
:唤醒一个等待在Condition上的线程,并使其从await()方法返回。signalAll()
:唤醒所有等待在Condition上的线程,并使它们从await()方法返回。使用示例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void doSomething() {
lock.lock();
try {
// 等待条件
condition.await();
// 执行其他操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//由其它线程来调用这个方法,唤醒线程
public void notifyThread() {
lock.lock();
try {
// 唤醒线程
condition.signal();
} finally {
lock.unlock();
}
}
}