ReentrantLock 细品底层源码

前言

ReentrantLock很多读者都了解过,笔者亦曾听过,作为了并发中与synchronize关键字对齐的同步安全保障,笔者认为还是有必要去深入了解一下。

正文

要了解ReentrantLock,就得知道AQS,lock实现的核心机制就是基于aqs框架,读者可自行查阅其他资料,或观看笔者关于aqs的介绍

常量

首先来观察一下类的常量

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;

ReentrantLock实现了lock接口和Serializable序列化接口,常量的话一个是序列化id,一个是内部类的对象sync。观察一下这个内部类

  abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

观察到这个内部类了继承了AbstractQueuedSynchronizer简写AQS这个类。

构造方法

ReentrantLock一共有两个构造方法

// 默认构造方法,创建非公平的同步锁
 public ReentrantLock() {
        sync = new NonfairSync();
    }
    // 含参构造,可创建非公平还是公平的锁
 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

NonfairSync和FairSync这两方法都是ReentrantLock内部类的默认构造方法,ReentrantLock一共有三个内部类,之前介绍过Sync内部类,现在看下剩下的两个内部类

 /**
     * Sync object for non-fair locks
     */
    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() {
        // 运用到cas理论,将状态值置为1
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
		// 覆写AQS父类的tryAcquire方法
        protected final boolean tryAcquire(int acquires) {
        	// 调用非公平尝试竞争锁方法
            return nonfairTryAcquire(acquires);
        }
    }

    /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
        	// 调用父类AQS的独占锁获取方法
            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();
			// 父类AQS的方法,获取状态
            int c = getState();
            if (c == 0) {
            // AQS的队列是否有前驱节点
                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中调用了很多父类AQS中的方法,所以读者在研究ReentrantLock时,优先研究AQS的代码。

tryAcquire方法剖析

读者可能好奇为何不去剖析lock方法中的acquire方法,而来剖析tryAcquire方法,因为acquire方法是调用父类AQS的acquire方法,笔者在AQS的文章中详细介绍了独占锁和共享锁的获取去释放,本文详细介绍一下AQS中一些方法在子类的覆写。

     */
        final boolean nonfairTryAcquire(int acquires) {
        	// 获取当前线程
            final Thread current = Thread.currentThread();
            // 获取资源状态
            int c = getState();
            if (c == 0) {
            // cas成功,说明获取到锁,调用父类的setExclusiveOwnerThread方法
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 如果当前线程已经获取到锁,就更新state的值
            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;
        }

tryRelease方法剖析

  protected final boolean tryRelease(int releases) {
  			// 获取当前状态值减去入参releases值(1)
            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;
        }

可能有读者好奇为何笔者能断定入参releases值为1。
因为在代码中参数值写死了

  public void unlock() {
        sync.release(1);
    }

调用内部类的release方法,而sync内部类release方法是父类AQS的中final修饰的方法,禁止子类覆写

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

父类中会调用子类覆写的tryRelease方法,这也就是为何笔者敢断言入参值为1.

后话

如果了解AQS的话,发现ReentrantLock的代码理解简单,基于AQS的独占锁方式实现,覆写了父类的tryAcquire以及tryRelease方法,增减了公平锁以及非公平锁的机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值