Java多线程之ReentrantLock实现原理和源码分析(二)

章节概览、

1、ReentrantLock概述

ReentrantLock字面含义是可重入的互斥锁,实现了和synchronize关键字一样的独占锁功能。但是ReentrantLock使用的是自旋锁,通过CAS硬件原语指令实现的轻量级的锁,不会引起上下文切换。而Synchronize关键字是重量级的且是独占的悲观锁。在使用过程中,会引起上下文切换。同时ReentrantLock增加了一些高级的扩展功能,比如它可以实现公平锁,同时也可以绑定多个Conditon。每个Condition都维护了自己的阻塞队列。而不像wait/notify只维护一个队列而引发的唤醒错误的情况。


2、ReentrantLock的实现模板

ReentrantLock锁保证线程安全的实现模板如下:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   public void m() {
      lock.lock();  // block until condition holds
      try {
        // ... method body
      } finally {
        lock.unlock()
      }
    }
  }}

从默认的实现模板可以看出,其使用方式和Synchronize不同,首先其不需要同步的对象。而Synchronize需要一个同步锁对象。其次ReentrantLock是显示调用。用户自己可以控制从哪里开始,到哪里结束。而Synchronize只能同步代码块等。具体ReentrantLock的使用方式,请参考:可重入锁:ReentrantLock理解使用


3、ReentrantLock类图结构

ReentrantLock的类图结构如下:
在这里插入图片描述
从类的继承图上可以看出,ReentrantLock实现了Lock接口。同时其具有3个内部类,分别为Sync,NonfairSync,FairSync。而Sync有继承了AbstractQueuedSynchronize,这个就是我们常说的AQS。而NonFairSync继承Sync也就是我们所说的非公平锁。FairSync继承Sync实现了公平锁功能。

3.1、Lock接口源码描述
/**
 * @see ReentrantLock
 * @see Condition
 * @see ReadWriteLock
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface Lock {
    //获取锁
    void lock();
    //获取可中断锁
    void lockInterruptibly() throws InterruptedException;
    //尝试获取锁,立刻返回
    boolean tryLock();
    //在指定的时间范围内尝试获取锁,立即返回
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    //释放当前锁
    void unlock();
    //获取一个条件对象,用于线程等待唤醒
    Condition newCondition();
}

3.2、ReentrantLock的构造方法和成员

ReentranLock的构造方法成员参数如下:

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
   // 同步器的引用
    private final Sync sync;
   //非公平锁的构造函数
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    // 公平锁的构造函数
	public ReentrantLock(boolean fair) {
	// 三目运算符,如果为true 则为公平锁,反之为非公平锁
        sync = fair ? new FairSync() : new NonfairSync();
    }
}
3.3、Sync抽象类核心源码分析
// 静态的抽象内部类,修饰符为default,仅能在当前类所在的包中使用
// 继承了AbstractQueuedSynchronizer抽象类
abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
        
        /**
         * 抽象方法,获取当前锁,具体实现在子类中实现
         */
        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();
        // 获取当前系统的同步状态变量state
            int c = getState();
        // 判断当前的系统状态变量是否为0
        // 其中0,表示当前锁空闲。大于0表示当前系统锁已经被占用
            if (c == 0) {
            // 利用CAS原语设置当前state的状态值为1,立刻返回
                if (compareAndSetState(0, acquires)) {
                // 如果设置成功,表示已经获取当前锁,设置当前锁的拥有者为当前线程
                    setExclusiveOwnerThread(current);
                 // 返回获取成功
                    return true;
                }
            }
            // 如果当前是state不为0,判断当前线程是否已经拥有锁
            // 因为ReentrantLock是可冲入锁,所以如果当前线程已经拥有锁的情况下,可以再次使用该锁
            else if (current == getExclusiveOwnerThread()) {
            // 如果当前线程已经拥有锁,则把当前的 state + 1
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
            // 设置当前state的值
                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 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
        }
    }
3.4、NonfairSync类核心源码分析

NonfairSync主要是非公平锁的实现:

 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的方法
         */
        final void lock() {
        // 尝试获取锁lock() 方法
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            // 获取锁失败,加入到等待队列
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
3.5、FairSync类核心源码分析

以下是公平锁实现的核心源码

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        // 公平锁没有尝试的去获取锁的功能,直接调用acquire()方法
        final void lock() {
            acquire(1);
        }
        
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 公平锁中
            if (c == 0) {
            // 通过hasQueuedPredecessors判断当前线程是否为head的next节点。
            // 如果是的话,则尝试获取锁
            // 获取锁成功进行一系列的设置
                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源码分析:

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());
    }

这里的逻辑判断稍微有些复杂,我们整理下思路。
return返回的代码,可以将逻辑判断分为2部分,只要其中有一个为true,则返回为true。当返回为真的话, if (!hasQueuedPredecessors() && compareAndSetState(0, acquires))就失败。我们详细分析下:

  • h != t && ((s = h.next) == null

这个逻辑成立的一种可能是head指向头结点,tail此时还为null。考虑这种情况:当其他某个线程去获取锁失败,需构造一个结点加入同步队列中(假设此时同步队列为空),在添加的时候,需要先创建一个无意义傀儡头结点(在AQS的enq方法中,这是个自旋CAS操作),有可能在将head指向此傀儡结点完毕之后,还未将tail指向此结点。很明显,此线程时间上优于当前线程,所以,返回true,表示有等待中的线程且比自己来的还早。

  • s.thread != Thread.currentThread()

当前的线程和Head的next线程是否相等。如果不相等返回为true。说明当前线程不是head的next节点,因为在公平锁中,只有head的next节点才有权利去获取锁


4、 结语

至此ReentrantLock的类结构和核心源码做了简单分析。通过分析我们可以了解到ReentrantLock的整体结构,构造函数,以及当前类的成员变量。以及ReentrantLock的内部类的情况,内部类的继承情况。ReentrantLock中使用了大量的内部类,很好的做到了封装性。对细节实现部分进行了封装。用户只需要调用构造方法,而不用去关系其内部的实现情况。同时也是设计模式中的迪米特法则:最少知道原则的体现。实现了很好的高内聚减少了耦合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值