JUC源码阅读-ReentrantLock

本文详细解析了ReentrantLock的工作原理,包括公平锁和非公平锁的机制,以及与synchronized关键字的区别。ReentrantLock提供了更灵活的锁管理和条件变量支持。

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

ReentrantLock可重入锁,是一种递归无阻塞的同步机制。它可以等同与synchornized实现同步。并且提供了比synchornized更为灵活的加锁机制。
ReentrantLock提供了公平锁FairSync和非公平锁NonfairSync两种机制。

 

  1. 公平锁,锁的获取是有序的
  2. 非公平锁,获取锁不是有序的,但效率比公平锁高,在多线程下,吞吐量较高。

ReentrantLock实现了Lock接口,内部基于Sync实现。

Sync类

Sync是ReentrantLock中的内部静态类,它继承了AbstractQueuedSynchronizer类,它使用AbstractQueuedSynchronizer类中state字段表示当前锁的持有数量,从而实现重入功能。其源码如下:

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();
            //如果为0,说明是空闲状态
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    //将锁设置为当前线程所有。
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //不是空闲状态,判断当前线程是否是持有锁的线程,如果是,线程重入,持有锁的数量 + acquires
            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;
            //如果减去持有锁数量后,同步状态为0,说明为空闲状态,设置为空闲状态
            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();
        }

        //Condition条件
        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
        }
    }

此段代码中重点关注非公平锁获取方式以及释放锁方法,非公平锁获取方式主要流程如下:

  1. 首先判断线程同步状态,如果为0,说明是空闲状态,就将锁设置为当前线程所有。
  2. 如果部位0,判断当前线程是否是持有锁的线程,如果是,则是线程重入,将持有锁的数量 + acquires,实现线程重入的特性。
  3. 如果获取到锁,返回true,否则返回false.

释放锁流程如下:

  1. 首先将state减去release;
  2. 判断当前线程是否是持有锁的线程,如果不是则抛出异常。
  3. 判断state是否为0,为0说明锁为空闲状态,将锁持有的线程设置为努力了,其他线程就可以获取同步状态了。
  4. 同步状态释放成功,则返回true,否则返回false.

 

NonfairSync类

NonfairSync 是 ReentrantLock 的内部静态类,实现 Sync 抽象类,是非公平锁实现类。其源码如下:

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        //实现Sync类的lock方法
        final void lock() {
            //CAS ,如果设置状态成功,将当前线程设置为持有锁线程
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                //失败,尝试获取锁,此方法在AbstractQueuedSynchronizer中。
                acquire(1);
        }
        
        //重写AbstractQueuedSynchronizer类的tryAcquire方法
        protected final boolean tryAcquire(int acquires) {
            //非公平锁获取
            return nonfairTryAcquire(acquires);
        }
    }

FairSync类

NonfairSync 是 ReentrantLock 的内部静态类,实现 Sync 抽象类,是公平锁实现类。其源码如下:

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.
         */
        //重写AQS的方法,尝试获取公平锁
        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方法,此方法在AQS类中,主要作用是判断当前线程节点是否有前置等待获取锁的线程节点。其源码如下:

//该方法主要是判断当前线程是否位于 CLH 同步队列中的第一个。如果是则返回 true ,否则返回 false 。
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());
    }

ReentrantLock

ReentrantLock的实现方法,基本上是对Syncnn内部类的调用,其主要方法包括以下几个:

  1. ReentrantLock()无参构造,创建一个非公平锁的重入锁。
  2. ReentrantLock(boolean fair) 根据fair创建一个重入锁,fair为true为公平锁,false为非公平锁。
  3. lock() 获取锁。
  4. tryLock()尝试获取锁。
  5. unLock()释放锁。
  6. newCondition()条件。

源码:

//创建非公平锁
 public ReentrantLock() {
        sync = new NonfairSync();
    }

    //根据fair创建公平锁或非公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    //获取锁,立即返回
    public void lock() {
        sync.lock();
    }
//尝试获取锁,实现时,希望快速获取锁,所以即使fair= true即为公平锁的情况下依然使用非公平锁获取的实现。
public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
//带有超时 的获取锁,此方法按照fair实现的方式获取锁。
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

//释放锁
public void unlock() {
        sync.release(1);
    }

      //Condition条件
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

注:tryLock()方法在使用时,即使时公平锁的实现,也会调用非公平锁的方法快速获取锁。这也是nonfairTyrAcquire()方法在Sync写在Sync而非NonfairSync中的原因。

ReentrantLock和Synchronized的区别

ReentrantLoc和synchronized两者都能够实现线程通过,两者之间的区别主要在以下几个方面:

  1. 与 synchronized 相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。
  2. ReentrantLock 还提供了条件 Condition ,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock 更加适合。
  3. ReentrantLock 提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而 synchronized 则一旦进入锁请求要么成功要么阻塞,所以相比 synchronized 而言,ReentrantLock会不容易产生死锁些。
  4. ReentrantLock 支持更加灵活的同步代码块,但是使用 synchronized 时,只能在同一个 synchronized 块结构中获取和释放。注意,ReentrantLock 的锁释放一定要在 finally 中处理,否则可能会产生严重的后果。
  5. ReentrantLock 支持中断处理,且性能较 synchronized 会好些。

此段来自芋道源码【死磕 Java 并发】—– J.U.C 之重入锁:ReentrantLock 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值