ReentrantLock笔记

本文详细剖析了Java并发包中的ReentrantLock类,包括Lock接口的功能定义、ReentrantLock的内部实现原理及其背后的AQS机制。特别关注了公平锁与非公平锁的区别,并通过源码分析展示了线程如何获取与释放锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看了Java的并发包concurrent的ReentrantLock类源码,做下笔记。

一、Lock接口

//在java的concurrent包中有个Lock接口,用来规范定义锁的行为
public interface Lock{

    /**
    * 获得锁,如果锁不可用,当前线程会一直休眠(即使当前线程被其他线程中断的情况也会一直休眠)直到锁可获得。
    */
    void lock();

    /**
    * 获得锁,如果锁不可用,当前线程会一直休眠直到当前线程被其他线程中断则会抛出InterruptedException异常。
    */
    void lockInterruptibly() throws InterruptedException;

    /**
    * 锁是空闲的则立即返回true,如果锁是不可用的则立即返回false。
    */
    boolean tryLock();


    /**
    * 如果锁可用,获得锁,返回true;
    * 如果锁不可用,当前线程会一直休眠直到当前线程被其他线程中断则会抛出InterruptedException异常;
    * 如果已经超过指定的时间,还没获得锁,返回false。
    */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
    /*
    * 释放锁
    */
    void unlock();

    /*
    * 返回一个绑定到当前Lock实例的Condition实例。
    */
    Condition newCondition();
}

..

二、ReentrantLock源码

//===============================================================

进入主题,ReentrantLock源码

public class ReentrantLock implements Lock, java.io.Serializable {

    private final Sync sync;

    //同步器(很多功能继承于抽象类AbstractQueuedSynchronizer)
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        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) {
                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;
        }

        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 this lock instance from a stream.
         * @param s the stream
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    //非公平锁的同步器
    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() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires); //调用父类Sync.nonfairTryAcquire(int acquires)
        }
    }

    //公平锁的同步器
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        //尝试获得锁,成功返回true,失败则返回false
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();//调用了AbstractQueuedSynchronizer.getStatus()==>>state
            if (c == 0) { //state为0,表示没有线程获得锁
                //!hasQueuedPredecessors():队列里没有排在当前线程之前的线程,
                //compareAndSetState(0, acquires):则将锁的状态设置为已被获取
                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;
        }
    }

}

..

三、AQS

//==========================================

其中锁背后真正的英雄是AQS(AbstractQueuedSynchronizer类,下面简称为AQS)的一些代码,当然,还有一些方法没写上来。

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronzier{

    static final class Node {
        //表示线程已经取消
        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        //表示线程需要唤醒
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;

        //waitStatus的值有:CANCELLED,SIGNAL,CONDITION,PROPAGATE
        volatile int waitStatus;
        //省略其他代码
    }
   
    //同步器的状态,volatile标识则保证了属性值的对多线程的可见性
    private volatile int state;

    //获得arg个许可
    public final void acquire(int arg) {
        //Node.EXCLUSIVE:线程节点标记为独占模式
        if ( ! tryAcquire(arg)  && acquireQueued(addWaiter(Node.EXCLUSIVE), arg) ) {

            selfInterrupt();

        }
    }

    //创建一个等待线程,插入队列
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {//队尾不为空,则表示队尾已经被初始化
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {//将当前线程节点衔接到队尾
                pred.next = node;
                return node;
            }
        }
        //代码执行到这里,表示队尾为空,即队尾还没被初始化
        enq(node);
        return node;
    }

   public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }


    //线程节点入队
    private Node enq(final Node node) {
        //如果队列还没被初始化,则初始化队头为(new Node()),队尾也指向队头
        //然后将线程节点衔接到队尾
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))//使用CAS原子操作设置队头
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {//使用CAS原子操作设置队尾
                    t.next = node;
                    return t;
                }
            }
        }
    }

    //队列里有优先获得锁的线程
    public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        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());
            //队头head和队尾tail的指向不同
            //(因为队列还没初始化时,head和tail都为null,队列刚初始化时,它们的指向也是相同的)
            //队尾没有在排队的线程或者排在队尾的线程是其他线程
    }

    排队获得锁(CLH锁)
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();//排在线程节点前面的节点
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //如果前趋节点p是正在阻塞等待锁的状态(shouldParkAfterFailedAcquire方法),则当前线程挂起(parkAndCheckInterrupt方法)。
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

    /**
     * Checks and updates status for a node that failed to acquire.
     * Returns true if thread should block. This is the main signal
     * control in all acquire loops.  Requires that pred == node.prev
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL) //SIGNAL:-1,前趋节点的状态是需要唤醒的状态
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) { //waitStatus > 0,即是前趋节点的锁被取消的值(Node.CANCELLED,值是1)
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev; //修改当前节点的前趋节点,往前移一个节点。
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //前趋节点设置成需要唤醒的状态。
        }
        return false;
    }

    /**
     * Convenience method to park and then check if interrupted
     *
     * @return {@code true} if interrupted
     */
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

    private static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }           

}
//==========================================================

一.公平锁FairSync

使用公平锁,创建ReentrantLock

ReentrantLock fairLock = new java.util.concurrent.locks.ReentrantLock(true);
		fairLock.lock();
		fairLock.unlock();

ReentrantLock.lock();==>>FairSync.lock()==>>AQS.acquire(1)实现:

1.!FairSync.tryAcquire(int acquires)//1为真,则表示没有获得锁,注意,前面有个"!"(非逻辑)
并且
2.AQS.acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
   //Node.EXCLUSIVE就独占式锁,因为只有一个线程能获得锁,所以使用独占式锁;
   //比如信号量Semaphore可以设置多个许可供给多个线程获得,则Semaphore会使用共享式锁Node.SHARED

(1和2)都为真 ==>>AQS.selfInterrupt()

//============================================================
FairSync.tryAcquire(int acquires)实现:

1.状态为0,则表示锁是空闲的
1.1 !AQS.hasQueuedPredecessors() //1.1为真,则表示等待队列里没有优先等待锁的线程节点
并且
1.2 AQS.compareAndSetState(0, 1) //使用CAS原子操作设置state值由0变为1
(1.1和1.2)都为真 ==>> 当前线程获得锁成功返回true。

2.状态不为0,则表示锁已被某个线程获得,
2.1判断锁的持有者是否当前线程
2.1.1是当前线程,则表示线程重复获得锁,将状态值state增加acquires,获得锁成功返回true。
2.1.2不是当前线程,尝试获得锁失败,返回false。

3. 返回false

//============================================================
总结:公平锁
线程请求获取锁时,判断状态state的值
1.当状态state为0时(即锁还没被线程占有),
1.1判断队列里有没前任优先线程节点,即,在当前线程之前有没其他线程在等待锁的释放
1.1.1如果没有优先线程,则当前线程可以获得锁
1.1.2如果有优先线程,则当前线程衔接在队尾阻塞等待锁空闲,等待其他线程唤醒来获得锁
2.当状态state不为0时,说明锁已经被某个线程占有,判断占有线程是否当前线程
2.1是当前线程,则线程可重复获得锁,将状态state值加1,表示线程又一次重复获得锁
2.2不是当前线程,则当前线程衔接在队尾阻塞等待锁空闲,等待其他线程唤醒来获得锁

//============================================================
总结:非公平锁
线程请求获取锁时,首先比其他线程优先获得锁(抢占式地,直接使用CAS(调用:compareAndSetState(0, 1))设置状态state值),

总之,如有刚好锁空闲时,非公平锁会优先获得锁,锁已被占有时,再衔接到队尾,排队等待锁。


不管是公平锁,还是非公平锁,没获得锁进入阻塞排队时,AQS都会使用CLH队列(对应到上面的acquireQueued方法),
当前线程会判断它的前趋节点,如果前趋节点的等待状态waitStatus是SIGNAL,则说明前趋节点也是在等待锁挂起中,那当前线程也会挂起等待锁;
如果前趋节点是队头head,则说明已经轮到当前线程获得锁;

释放锁时,会从队头的后继节点开始,唤醒挂起的后继节点,则后继获得锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值