JUC并发编程-ReentrantLock原理

ReentrantLock

  1. Reentrantlock 是并发包中一个可重入的锁,是基于AQS(AbstractQueuedSynchronized)实现的,它有公平锁和不公平锁两种实现方式。

  2. Reentranlock 中有一个内部抽象类 Sync 继承自 AbstractQueuedSynchronized ,主要是它来实现锁的功能, Sync 在 ReentrantLock 中有两种实现类:NonfairSync、FairSync,正好对应了ReentrantLock的非公平锁、公平锁两大类型。Reentranlock 默认实现为非公平锁,在高竞争的条件下有更好的性能。

与synchronized区别

  • synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。

  • synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

  • synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。

  • jdk8之前,Synchronized是重量级锁,ReentrantLock相比较比较轻量级。

定义

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    private final Sync sync;//同步器
	//抽象的aqs实现
    abstract static class Sync extends AbstractQueuedSynchronizer {
        //*****省略********
    }
	//非公平
    static final class NonfairSync extends Sync {
        //*****省略********
    }
	//公平
    static final class FairSync extends Sync {
        //*****省略********
    }
	//构造方法
    public ReentrantLock() {
        sync = new NonfairSync();//默认非公平锁
    }
    public ReentrantLock(boolean fair) {//设置公平非公平
        sync = fair ? new FairSync() : new NonfairSync();
    }
	//调用sync的lock方法。其在NonfairSync和FairSync中根据是否公平实现
    public void lock() {
        sync.lock();
    }
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
	//尝试获取锁
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
	//解锁
    public void unlock() {
        sync.release(1);
    }
    public Condition newCondition() {
        return sync.newCondition();
    }
}

JUC并发编程-ReentrantLock原理_第1张图片

Sync

ReentrantLock基于AQS实现。对于排他锁来说,我们一般只需要是实现AQS的两个方法

  • tryAcquire
  • tryRelease

当需要获得锁时,调用AQS的acquire即可。

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

     //抽象函数,根据公平非公平交给子类实现
    abstract void lock();

    //非公平尝试获取锁,之所以写在这,是因为在公平情况下,当TryLock的时候,即使是设置的公平但仍然会调用这个非公平的方法,可以理解为是TryLock的机制吧
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {//与公平锁的区别,不需要判断aqs队列的情况,直接cas
                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;
    }
	//****省略******
}

非公锁实现

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

    final void lock() {
        if (compareAndSetState(0, 1))//非公平情况下会直接通过cas尝试获取锁
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

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

公平锁实现

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);//acquire被定义在aqs中,其首先调用了下面的tryAcquire方法尝试获取锁
    }
	//尝试获取锁
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();//获取state
        if (c == 0) {//自由状态
            /**
            因为是公平锁,首先检查之前是否需要排队
            1.hasQueuedPredecessors检查是否已经有线程正在等待资源释放
            2.如果没有其他线程正在等锁,则利用cas设置state获取锁
            */
            if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);//设置当前线程为获取到锁的线程
                return true;
            }
            /**hasQueuedPredecessors源码,定义在父类AQS中了
            AQS中维护了一个双向链表,存储了所有的尝试获取锁的线程
             public final boolean hasQueuedPredecessors() {
                Node t = tail; //队列尾部
                Node h = head;//队列头部
                Node s;
                //如果除了本线程之外,在队列中只有一个线程则返回true,否则false
                //意思是,如果在队列中存在线程正在等,则返回true
               	//这个代码很nb了,考虑了各种情况
               	
                return h != t &&
                    ((s = h.next) == null || s.thread != Thread.currentThread());
             1.当aqs的队列未初始化时,head和tail都是null,返回false,说明此时没有线程排队,可以尝试获取			
             2. s.thread != Thread.currentThread()判断的是当已经入队的线程在进行自旋时调用了tryAquire,s.thread != Thread.currentThread()不成立,所以返回false,进一步执行cas尝试获取锁
                    
   			 }
            */
        }
        //如果state不等于0,说明已经有线程获取到锁,判断当前线程是否是持有锁的那个线程
        //即 可重入   
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);//重入锁  给state加1,表示重入的次数
            return true;
        }
        return false;//尝试获取锁失败
    }
}

你可能感兴趣的:(java)