乐趣区

关于java:认真学java锁可重入锁ReentrantLock

学习记录,有不对的或更好见解,还请多多交换

参考

  • why-use-a-reentrantlock-if-one-can-use-synchronizedthis
  • ReentrantLock (Java Platform SE 8)

需理解的常识

synchronized
不明确,能够看以下文章,缓缓看,沉下心

  • Synchronized Methods (The Java™ Tutorials > Essential Classes > Concurrency)
  • Concurrency, Visibility, and Memory (不懂 volatile 的先看这篇)

看完以上文章,就不必看本文了????

ReenentrantLock 概述

ReentrantLock 是对 synchronized 的扩大。突出的特点就是 可重入 ,更灵便。可重入是指什么?那反过来问不可重入是什么?
不可重入顾名思义就是不能够反复进入,具体指的是在没有开释锁的状况下,不能够再获取锁,否则就会造成死锁 dead lock。(很多名词都是英文翻译过去的,所以英文很重要,不然获取的常识就是二手的)
谬误例子如下:

public void test(Object object){synchronized(object){           // first lock
        synchronized(object){       // second lock
            //do something
        }
    }
}

下面的 second lock 永远也获取不到锁,所以造成了死锁,程序就卡死在第二次加锁。

次要原理:ReentrantLock 类的外部有个 Sync 类继承了 AbstractQueuedSynchronizer(AQS 封装了多线程下队列的同步操作)对锁操作进行了封装。NonfairSync 和 FairSync 继承了 Sync 类,并重载了各自的 tryAcquire 办法。通过不同的结构器 ReentrantLock(),ReentrantLock(boolean fair)来实例化不同的 Sync 对象,以创立偏心可重入锁 / 不偏心可重入锁对象,后续的所有操作都是基于这个 lock 对象进行操作。

而对于 ReentrantLock 来说,它提供了在锁没开释时,能够再次获取锁的办法 tryLock。先来看看 ReentrantLock 中重要的办法及含意:

  1. lock 和 synchronized 相似,仅当没有 其余 线程持有该对象的锁时,能力加锁胜利。在其余线程持有锁期间,该线程会进去睡眠状态,(dormant),直到获取到锁。区别在于:若本人对本人再次加锁,是能够取得锁的。(可重入)(注:synchronized 对本人加锁两次是死锁)
  2. 结构器中的参数 fairness,设为 true 时,锁会优先让期待最长工夫的线程持有;若为 false(默认为 false),则不保障线程持有锁的先后顺序。
    留神:fairness 为 true,会重大影响整体性能。而且 fairness 并不保障线程的调度的偏心(不保障每个线程均匀获取锁,只是让排队最长工夫先的获取而已)
  3. tryLock办法 不受 fairness 设置影响 。即便有其余线程获取了锁,锁还是能够被持续获取的, 最大反对递归 2147483647 次(int 的最大整数)。( 可重入

注 tryLock()在没取得锁时,不会像 lock()一样进入睡眠期待;tryLock(long timeout, TimeUnit unit)会睡眠期待 timeout 时长,同时会 fairness 规定

  1. unlock开释本人取得的锁,使锁的次数 -1。

源码:
1、Sync外部类 nonfairTryAcquire 办法

final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {      // 初始态,没有线程持有锁时
        if (compareAndSetState(0, acquires)) {  // 见下文 2
            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;
}

2、AQS 中的 compareAndSetState(int expect, int update) 办法

protected final boolean compareAndSetState(int expect, int update) {return STATE.compareAndSet(this, expect, update);   // 见下文 3
}

3、本地办法(C++ 编写的)compareAndSet 位于 java.lang.invoke.VarHandle 下(常说的 CAS)
官网 api 链接:CAS

如果 witness value == exceptedValue,则赋新值 newVaule。其中 witness value 为 main memory 中该变量的值(getVolatile 获取);赋新值通过 setVolatile 赋值。return true,赋值胜利。return false, 变量被其余线程扭转了,赋值失败。

4、NonfairSync 类的 tryAcquire 就是调用 Sync 中 nonfairTryAcquire(acquires);
5、FairSync 中的 tryAcquire

/**
 * 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) {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;
}

6、hasQueuedPredecessors(AQS 中)

/*
* Queries whether any threads have been waiting to acquire longer than the current thread.
* 翻译:查看是否有其余想获取锁的线程比以后线程等的长(偏心锁的准则:先服务等得久的)*/
源码略,因为须要先看懂 AQS 这个类的实现机制
退出移动版