ReentranLock源码分析

介绍

与synchronized对比:

  • ReentranLock 是SDK实现的,实现了Lock接口,可重入,可相应中断,加锁解锁需手动实现
  • ReentranLock 可通过构造方法入参选择公平锁与不公平锁,默认不公平锁
  • synchronized 是jvm实现的,可重入,不可相应中断,加锁解锁自动实现

使用

贴代码例子:

   private final Lock rtl = new ReentrantLock();

    public void test(){
        try {
            rtl.lock();
            System.out.println("lock test");
        } finally {
            rtl.unlock();
        }
    }

源码分析

ReentranLock 的所有功能均由内部静态类Sync实现,FairSync 与 NonfairSync均继承了该类。

整体以一个int类型的state与一个队列实现,state代表了锁的状态,队列则存放了等待获取锁的线程节点。

下面以NonfairSync为例分析

lock()

  1. 调用sync.lock

    public void lock() {
            sync.lock();
    }
    

    非公平锁,sync 为 NonfairSync。

  2. NonfairSync的**lock()**方法

    	final void lock() {
            	// 若当前state为0,则代表当前锁可获得,
                if (compareAndSetState(0, 1))
                    // 若获取成功,则将当前线程设为拥有锁的线程
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }
    

    若当前state为0,则代表当前锁可获得,若获取成功,则将当前线程设为拥有锁的线程。

    (非公平锁在lock时首先就会去尝试获取锁,而公平锁则会直接进入acquire方法,不会首先尝试获取锁,这是两者第一个区别点)

    protected final boolean compareAndSetState(int expect, int update) {
        	// 调用底层native方法
            // See below for intrinsics setup to support this
            return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
        }
    

    若state不为0,则进入acquire 方法

  3. NonfairSync的acquire(1)

    public final void acquire(int arg) {
        	// 首先尝试获取锁
            if (!tryAcquire(arg) &&
                // 获取失败,则将当前线程加入到获取队列中
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                // 中断线程
                selfInterrupt();
        }
    

    首先尝试获取锁,若获取失败,则加入队列中

    protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
    

    获取锁通过检查Sync的state来判断锁状态

    	final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
            	// c = 0,尝试获取锁,(公平锁还会检查是否是队列头)
                if (c == 0) {
                    if (compareAndSetState(0, acquires)) {
                        // 设置当前线程为拥有锁线程
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
            	// 如果当前线程拥有锁,则state + 1,重入锁的实现
                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;
            }
    
  4. NonfairSync的 acquireQueued

    若获取锁失败,则加入锁获取队列,首先以当前线程建立Node节点,addWaiter:

    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;
       }
    
    private Node enq(final Node node) {
    		// 重复插入尾部,直到成功
            for (;;) {
                Node t = tail;
                if (t == null) { // Must initialize
                    // 创建初始节点,可能被其它线程创建
                    if (compareAndSetHead(new Node()))
                        tail = head;
                } else {
                    node.prev = t;
                    // 可能被其它线程抢占尾节点
                    if (compareAndSetTail(t, node)) {
                        t.next = node;
                        return t;
                    }
                }
            }
        }
    

    此时,已将当前线程节点加入到等待队列,尝试在队列中获取锁,acquireQueued()

        final boolean acquireQueued(final Node node, int arg) {
            boolean failed = true;
            try {
                boolean interrupted = false;
                for (;;) {
                    final Node p = node.predecessor();
                    // 如果node节点的前置节点为头节点,并且node节点获取锁成功,则结束循环
                    if (p == head && tryAcquire(arg)) {
                        setHead(node);
                        p.next = null; // help GC
                        failed = false;
                        return interrupted;
                    }
                    // 首先检查节点的前置状态,是否需要阻塞当前线程
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        // 阻塞当前线程
                        parkAndCheckInterrupt())
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    
        private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
            int ws = pred.waitStatus;
            if (ws == Node.SIGNAL)
                // 前置节点的状态为-1,当前线程可直接阻塞
                return true;
            if (ws > 0) {
                // 过滤掉节点前的已经取消的节点,直到ws <= 0
                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;
        }
    
        private final boolean parkAndCheckInterrupt() {
            // 阻塞当前线程,比较重要的一个时机
            LockSupport.park(this);
            return Thread.interrupted();
        }
    

lock方法的整个过程,基本就是这样,线程由阻塞状态转为非阻塞状态,是在state为0时,头节点会唤醒下一节点的线程。

unlock() 方法后续分析

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值