可重入锁 ReentrantLock的部分源码解析

ReentrantLock 是 Java 中一个常用的锁实现,位于 java.util.concurrent.locks 包中。它提供了更灵活的锁机制,相比于 synchronized 关键字,ReentrantLock 提供了更多高级功能,例如可重入锁、可定时、可中断的获取锁等。
image

基本结构

ReentrantLock 实现了 Lock 接口,主要依赖一个同步器 AbstractQueuedSynchronizer(AQS)来完成锁的获取与释放。其内部有两个子类,分别是 NonfairSyncFairSync,它们分别对应非公平锁和公平锁的实现。

内部方法

ReentrantLock 类的源码主要分为以下几个部分:

  1. 类声明和字段

    • ReentrantLock 类实现了 Lock 接口,并且是可序列化的。
    • Sync 是一个抽象静态内部类,继承自 AbstractQueuedSynchronizer,用于实现锁的同步机制。
    • NonfairSyncFairSyncSync 的两个子类,分别实现了非公平锁和公平锁的逻辑。
  2. 构造方法

    • ReentrantLock():默认构造方法,默认创建一个非公平锁。
    • ReentrantLock(boolean fair):带有公平性参数的构造方法,根据参数创建公平锁或非公平锁。
  3. 锁的获取和释放方法

    • lock():获取锁,如果当前线程已经持有锁,则增加持有计数。
    • lockInterruptibly():获取锁,支持中断。
    • tryLock():尝试获取锁而不进行等待。直接使用 CAS 操作尝试更新状态
    • tryLock(long timeout, TimeUnit unit):在指定时间内尝试获取锁,支持中断。
    • unlock():释放锁,减少持有计数,如果计数为零则真正释放锁。
  4. 条件变量

    • newCondition():返回一个与此锁关联的 Condition 实例 ConditionObject ,用于线程间的协调,条件等待
  5. 锁状态查询方法

    • getHoldCount():查询当前线程持有锁的次数。
    • isHeldByCurrentThread():查询当前线程是否持有锁。
    • isLocked():查询锁是否被任何线程持有。
    • isFair():查询锁是否是公平锁。
    • getOwner():返回当前持有锁的线程。
    • hasQueuedThreads():查询是否有线程在等待获取锁。
    • hasQueuedThread(Thread thread):查询指定线程是否在等待获取锁。
    • getQueueLength():返回等待获取锁的线程数的估计值。
    • getQueuedThreads():返回等待获取锁的线程集合。
    • hasWaiters(Condition condition):查询是否有线程在等待指定的条件。
    • getWaitQueueLength(Condition condition):返回等待指定条件的线程数的估计值。
    • getWaitingThreads(Condition condition):返回等待指定条件的线程集合。
  6. 内部类 Sync

    • nonfairTryAcquire(int acquires):非公平地尝试获取锁。
    • tryRelease(int releases):尝试释放锁。
    • isHeldExclusively():查询当前线程是否独占锁。
    • newCondition():创建一个新的 ConditionObject 实例。
    • getOwner():返回当前持有锁的线程。
    • getHoldCount():返回当前线程持有锁的次数。
    • isLocked():查询锁是否被任何线程持有。
    • readObject(java.io.ObjectInputStream s):反序列化时重置锁的状态。
  7. 内部类 NonfairSyncFairSync

    • NonfairSync:实现非公平锁的 tryAcquire 方法。直接插入锁队列的最前面,容易导致“饥饿”现象。但性能更高,因为减少了很多竞争
    • FairSync:实现公平锁的 tryAcquire 方法,确保锁的获取是公平的。按照请求锁的顺序来分配锁,能保证线程不会饥饿
  8. 其他方法

    • toString():返回锁的状态信息。
  9. AQS 的作用

    • AbstractQueuedSynchronizer 是 ReentrantLock 实现的核心,它维护了一个 FIFO 队列,管理线程间的同步状态:
    • state: 同步状态。0 表示未锁定状态,1 或更高表示锁定状态。
    • CLH 队列: 用于管理等待线程。
    • condition queue: 用于管理在某个条件上等待的线程。

部分源码

Sync

//在ReentrantLock代码里,Sync其内部的抽象静态类,这个类继承了AbstractQueuedSynchronizer
//Sync 底下还有两个内部类,一个实现非公平,一个实现公平,都继承 Sync。所以说ReentrantLock既是公平锁也是非公平锁。很公平的给你选择
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
 
         //非公平的实现,调用了该方法
		 //可以发现该方法内部是直接进行CAS的compareAndSetState(0, acquires)来尝试更改状态为锁定
		 //并没有去查看等待队列,也并没有查看是否有比当前线程等待时间更久的
        @ReservedStackAccess
        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;
        }

		//持有锁的计数减少,当计数回到0时,表明当前线程不再持有该锁
        @ReservedStackAccess
        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() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            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
        }
    }

公平与非公平

	/**
	* 如上所述,非公平锁是调用了Sync的nonfairTryAcquire()方法实现
	* 非公平锁(NonfairSync) 的实现允许"插队",即后来的线程可以直接尝试获取锁,而不考虑等待队列中的其他线程。这种做法可以提高吞吐量但可能导致某些线程一直不能获取到锁
	*/
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    //公平锁:确保队列中等待时间最长的线程最先获得锁。在这种模式下,锁的获取会先检测队列是否为空,若不为空则排队。
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        //公平锁是会去判断等待队列,并且查看当前线程是不是等待时间最长的,然后再进行compareAndSetState来尝试改变状态
		//如果队列中有等待线程,则当前线程不能插队
        @ReservedStackAccess
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
			/**
			* hasQueuedPredecessors() 方法是公平锁机制的核心部分,这个方法的作用是检查当前线程是否有其他前驱线程
			*(通常是通过检查等待队列中是否存在等待更长时间的线程,实现公平性):
				实现逻辑:它检查同步队列中是否有其他线程在等待锁定,即判断是否有比当前线程更早进入队列的线程。
				如果 hasQueuedPredecessors() 返回 false,则意味着当前线程可以在公平条件下获取锁,即它要么是队列中的头线程,要么队列是空的。
			*/
                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;
        }
    }

    /**
	可重入锁提供了两个构造函数
	无参构造默认是创建非公平锁
	*/
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    //含参狗砸则通过是否创建公平锁的布尔值参数来创建对应的锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
	
	···
	···
	/**
	当我们实例化一个ReentrantLock对象后,lock()时直接调用acquire(1)来获取锁
	acquire()是AbstractQueuedSynchronizer提供的方法,内部是调用tryAcquire(arg)尝试获取,如果失败则进行线程排队,如下
		public final void acquire(int arg) {
        	if (!tryAcquire(arg) &&
            	acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            	selfInterrupt();
    	}
	所以实例化的ReentrantLock若是公平的,则调用FairSync的tryAcquire()自定义实现;若是非公平的,则调用NonfairSync的自定义tryAcquire()实现
	*/
	public void lock() {
        sync.acquire(1);
    }
	···
	···
	//释放锁
	public void unlock() {
        sync.release(1);
    }

当然再往里挖,就到了实现的核心AbstractQueuedSynchronizer抽象类了,想把这个再挖清楚,暂时我做不太到···

Condition 条件等待

Condition 是用于协调线程间通信的一种机制,它依赖于一个 Lock 实现来支持更复杂的线程同步方案。ReentrantLock 提供了对 Condition 对象的支持,允许线程在特定条件下等待和被唤醒。

//在 ReentrantLock 内部,Condition 是通过它的内部类 ConditionObject 来实现的。这是 AbstractQueuedSynchronizer 的一个嵌套类,用于条件等待
final ConditionObject newCondition() {
            return new ConditionObject();
        }
···
···
public Condition newCondition() {
        return sync.newCondition();
    }

创建 Condition

ReentrantLock 中,创建一个 Condition 对象是通过调用 newCondition() 方法实现的。例如:

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();

这会创建一个与该锁关联的 ConditionObject

ConditionObject 工作原理

  1. await() 方法

    当一个线程调用 await() 方法时,它将是以下流程:

    • 它必须持有锁,否则会抛出 IllegalMonitorStateException
    • 当前线程会释放锁,并将自己加入到条件队列中。
    • 进入等待状态,直到收到信号 (signal()signalAll()) 或被中断。
    • 线程被唤醒后,需要重新获取锁,线程能继续执行后续的代码。
  2. signal() 方法

    signal() 方法用于唤醒等待条件队列中的一个线程。流程如下:

    • 调用 signal() 的线程必须持有锁,否则会抛出 IllegalMonitorStateException
    • 唤醒队列中第一个等待的线程,使其从等待中恢复。
    • 被唤醒的线程会从条件队列移动到同步队列,以便尝试获取锁。
  3. signalAll() 方法

    类似于 signal(),但它会唤醒条件队列中所有的等待线程。

代码示例

一个简单的使用 ReentrantLockCondition 的示例可能如下:

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();

public void awaitCondition() throws InterruptedException {
    lock.lock();
    try {
        while (/* condition not met */) {
            condition.await();
        }
        // Do something once the condition is met
    } finally {
        lock.unlock();
    }
}

public void signalCondition() {
    lock.lock();
    try {
        // Update condition state
        condition.signal();
    } finally {
        lock.unlock();
    }
}

注意事项

  • 使用 Condition 之前,线程必须先获取相关的锁。
  • await()signal() 之间,条件的检查和状态的改变要在 lock 内保证同步。
  • signal() 通常在修改共享状态并使得某些条件变为真的地方调用。

不同于synchronized,synchronized是实例对象 或者 类class这样的单一监视器锁,它们的等待和唤醒是由顶级父类Object提供的唤醒和等待方法notify(),notifyAll(),wait()。而ReentrantLock可以绑定多个Condition条件等待,由Condition提供更加灵活的等待和唤醒,并且可中断等待 等等操作

ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();

//在这个例子中,两个不同的条件(可能是某种资源的两种状态)使用了两个不同的 Condition 对象。每个条件的等待和通知操作都是独立的,而不需要共享同一个内置条件(即与该对象关联的单一监视器锁)
// 某个线程:
lock.lock();
try {
    while (!someCondition()) {
        conditionA.await(); 
    }
    // 处理逻辑...
} finally {
    lock.unlock();
}

// 另一个线程:
lock.lock();
try {
    while (!otherCondition()) {
        conditionB.await();  
    }
    // 处理逻辑...
} finally {
    lock.unlock();
}

或者

ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();

public void waitForCondition() throws InterruptedException {
    lock.lock();
    try {
        // 某种条件不满足时,线程等待
        while (!conditionMet()) {
            condition.await();
        }
        // 在条件满足后执行的逻辑
    } finally {
        lock.unlock();
    }
}

public void signalCondition() {
    lock.lock();
    try {
        // 更改条件,然后通知等待的线程
        modifyConditions();
        condition.signalAll(); // 可以使用 signal() 唤醒一个线程,或使用 signalAll() 唤醒所有等待线程
    } finally {
        lock.unlock();
    }
}

private boolean conditionMet() {
    // 逻辑检查条件是否满足
    return true; // 示例值
}

private void modifyConditions() {
    // 更改条件的实际操作
}

posted @ 2024-11-27 14:59  J九木  阅读(106)  评论(0)    收藏  举报