多线程与高并发(六) Lock

  • 时间:
  • 浏览:11
  • 来源:大发uu快3_uu快3腾讯app_大发uu快3腾讯app

之后学习了要怎样使用synchronized关键字来实现同步访问,Java SE 5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁功能,它提供了与synchronized关键字例如的同步功能,统统在使用需要要显式地获取和释放锁。嘴笨 它缺少了(通过synchronized块将会最好的土办法所提供的)隐式获取释放锁的便捷性,而是却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized关键字所不具备的同步价值形式。

不同于synchronized是Java语言的关键字,是内置价值形式,Lock都有Java语言内置的,Lock是另另一一四个类,通过或多或少类都可不还可以实现同步访问。而是synchronized同步块执行完成将会遇到异常是锁会自动释放,而lock需要调用unlock()最好的土办法释放锁,而是在finally块中释放锁。

一、 Lock 接口

先看看lock接口定义了哪此最好的土办法:

void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();

这顶端lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。这四个最好的土办法都有用来获取锁的,那有哪此区别呢?

lock()最好的土办法是平常使用得最多的另另一一四个最好的土办法,统统用来获取锁。将会锁已被或多或少系统任务管理器获取,则进行听候。

tryLock()最好的土办法是有返回值的,它表示用来尝试获取锁,将会获取成功,则返回true,将会获取失败(即锁已被或多或少系统任务管理器获取),则返回false,也统统或多或少最好的土办法无论要怎样都有立即返回。在拿非要锁时不必总爱在那听候。

tryLock(long time, TimeUnit unit)最好的土办法和tryLock()最好的土办法是例如的,只不过区别在于或多或少最好的土办法在拿非要锁都有听候一定的时间,在时间期限之内将会还拿非要锁,就返回false。将会将会一开始英文英文拿到锁将会在听候期间内拿到了锁,则返回true。

lockInterruptibly()最好的土办法,当通过或多或少最好的土办法去获取锁时,将会系统任务管理器正在听候获取锁,则或多或少系统任务管理器可不还可以响应中断,即中断系统任务管理器的听候清况 。也就使说,当另另一一四个系统任务管理器一起通过lock.lockInterruptibly()想获取某个锁时,而是我我此时系统任务管理器A获取到了锁,而系统任务管理器B非要在听候,没有对系统任务管理器B调用threadB.interrupt()最好的土办法可不还可以中断系统任务管理器B的听候过程。

unLock()最好的土办法是用来释放锁的,这没哪此有点痛 需要讲的。

Condition newCondition() 是用于获取与lock绑定的听候通知组件,当前系统任务管理器需要获得了锁可不还可以进行听候,进行听候都有先释放锁,当再次获取锁时可不还可以从听候中返回。

Lock接口顶端的最好的土办法朋友 儿将会知道,接下来实现Lock的类ReentrantLock开始英文英文学起,发现ReentrantLock并没有几个代码,另外有另另一一四个很明显的特点是:基本上所有的最好的土办法的实现实际上都有调用了其静态内存类Sync中的最好的土办法,而Sync类继承了AbstractQueuedSynchronizer(AQS)。

朋友 儿先学AQS相关的知识

二、AQS

AQS(以下简称同步器)是用来构建锁和或多或少同步组件的基础框架,它的实现主要依赖另另一一四个int成员变量来表示同步清况 ,通过内置的FIFO队列来完成排队工作。

子类通过继承并实现它的抽象最好的土办法来管理同步清况 ,通过使用getState,setState以及compareAndSetState或多或少个多最好的土办法对同步清况 进行更改。子类推荐被定义为自定义同步组件的静态内控 类,同步器自身没有实现任何同步接口,它仅仅是定义了若干同步清况 的获取和释放最好的土办法来供自定义同步组件的使用,同步器既支持独占式获取同步清况 ,也都可不还可以支持共享式获取同步清况 ,之后就都可不还可以方便的实现不例如型的同步组件。

同步器是实现锁的关键,要实现锁功能,子类继承Lock,它定义了使用者与锁交互的接口,就像顶端那几个接口,而是实现却是通过同步器,同步器复杂性了锁的实现最好的土办法,实现了底层操作,如同步清况 管理,系统任务管理器的排队,听候和唤醒,而外面使用者去不必关心哪此细节。

2.1 同步器的接口

同步器的设计模式是基于模板最好的土办法,也统统说,使用者要继承同步器并重写指定的最好的土办法,而是将同步器组合在自定义同步器组合定义在自定义同步组件的实现中,并调用同步器提供的模板最好的土办法,而哪此模板最好的土办法将会调用使用者重写的最好的土办法。总结统统同步器将或多或少最好的土办法开放给子类进行重写,而同步器给同步组件所提供模板最好的土办法又会重新调用被子类所重写的最好的土办法

如在AQS含有此最好的土办法:

protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

而ReentrantLock中重写了最好的土办法:

那在AQS中的acquire调用了或多或少最好的土办法,这就相当于在父类定义了一套模板,哪此模板会调用或多或少可重写的最好的土办法,哪此可重写的最好的土办法具体的实现装入 了子类。

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

这统统模板最好的土办法最好的土办法的设计思路,如还有疑惑,都可不还可以去学习或多或少设计模式。

下面统统或多或少都可不还可以被重写的最好的土办法:

最好的土办法名称描述
protected boolean tryAcquire(int arg) 独占式获取同步清况 ,实现该最好的土办法需要查询当前清况 并判断同步清况 是有无符合预期,而是再进行CAS设置同步清况
protected boolean tryRelease(int arg) 独占式释放同步清况 ,听候获取同步清况 的系统任务管理器将有将会获取同步清况
protected int tryAcquireShared(int arg) 共享式获取同步清况 ,返回大于等于0的值,表示获取成功,反之,获取失败
protected boolean tryReleaseShared(int arg) 共享式释放同步清况
protected boolean isHeldExclusively() 当前同步器是有无在独占模式下被系统任务管理器占用,一般该最好的土办法表示是有无被当前系统任务管理器独占

实现自定义同步组件时,将会调用同步器提供的模板最好的土办法,哪此(次责)模板最好的土办法与描述

最好的土办法名称描述
void acquire(int arg) 独占式获取同步清况 ,将会当前系统任务管理器获取同步清况 成功,则由该最好的土办法返回,而是,将会进入同步队列听候,该最好的土办法将会调用重写的tryAcquire(int arg)最好的土办法
void acquireInterruptibly(int arg) 与acquire(int arg)相同,而是该最好的土办法响应中断,当前系统任务管理器未获取到同步清况 而进入同步队列中,将会当前系统任务管理器被中断,则该最好的土办法会抛出InterruptedException并返回
boolean tryAcquireNanos(int arg, long nanosTimeout) 在void acquireInterruptibly(int arg)的基础上增加了超时限制,将会当前系统任务管理器在超时时间内没有获取到同步清况 ,没有将会返回false,将会获取到了返回true
void acquireShared(int arg) 共享式的获取同步清况 ,将会当前系统任务管理器未获取到同步清况 ,将会进入同步队列听候,与独占式获取的主要区别是在同一时刻而是有无多个系统任务管理器获取到同步清况
void acquireSharedInterruptibly(int arg) 与acquireShared(int arg)相同,该最好的土办法响应中断
boolean tryAcquireSharedNanos(int arg, long nanosTimeout) 在acquireSharedInterruptibly(int arg)基础上增加了超时限制
boolean release(int arg) 独占式的释放同步清况 ,该最好的土办法会在释放同步清况 之后,将同步队列中第另另一一四个节点含有的系统任务管理器唤醒
boolean releaseShared(int arg) 共享式的释放同步清况
Collection<Thread> getQueuedThreads() 获取听候在同步队列上的系统任务管理器集合

同步器提供的模板最好的土办法基本上分为3类:

  1. 独占式获取与释放同步清况

  2. 共享式获取与释放同步清况

  3. 查询同步队列中的听候系统任务管理器清况 。

下面看另另一一四个例子:

public class Mutex implements Lock {
 private static class Sync extends AbstractQueuedSynchronizer {
    // Reports whether in locked state
    protected boolean isHeldExclusively() {
        return getState() == 1;
    }

    // Acquires the lock if state is zero
    public boolean tryAcquire(int acquires) {
        assert acquires == 1; // Otherwise unused
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    // Releases the lock by setting state to zero
    protected boolean tryRelease(int releases) {
        assert releases == 1; // Otherwise unused
        if (getState() == 0) throw new IllegalMonitorStateException();
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    // Provides a Condition
    Condition newCondition() {
        return new ConditionObject();
    }

    // Deserializes properly
    private void readObject(ObjectInputStream s)
            throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

private final Sync sync = new Sync();

@Override
public void lock() {
    sync.acquire(1);
}

@Override
public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

@Override
public boolean tryLock() {
    return sync.tryAcquire(1);
}

@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(time));
}

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

@Override
public Condition newCondition() {
    return sync.newCondition();
}
}

或多或少例子中,独占锁Mutex是另另一一四个自定义同步组件,它在同一时刻只允许另另一一四个系统任务管理器占有锁。Mutex中定义了另另一一四个静态内控 类,该内控 类继承了同步器并实现了独占式获取和释放同步清况 。在tryAcquire(int acquires)最好的土办法中,将会经过CAS设置成功(同步清况 设置为1),则代表获取了同步清况 ,而在tryRelease(int releases)最好的土办法中统统将同步清况 重置为0。用户使用Mutex时无须会直接和内控 同步器的实现打交道,统统调用Mutex提供的最好的土办法,在Mutex的实现中,以获取锁的lock()最好的土办法为例,只需要在最好的土办法实现中调用同步器的模板最好的土办法acquire(int args)即可,当前系统任务管理器调用该最好的土办法获取同步清况 失败都有被加入到同步队列中听候,之后就大大降低了实现另另一一四个可靠自定义同步组件的门槛。

2.2 同步队列

同步器依赖内控 的同步队列(另另一一四个FIFO双向队列)来完成同步清况 的管理,当前系统任务管理器获取同步清况 失败时,同步器会将当前系统任务管理器以及听候清况 等信息构造成为另另一一四个节点(Node)并将其加入同步队列,同都有阻塞当前系统任务管理器,当同步清况 释放时,会把首节点中的系统任务管理器唤醒,使其再次尝试获取同步清况 。

同步队列中的节点(Node)用来保存获取同步清况 失败的系统任务管理器引用、听候清况 以及前驱和后继节点。

volatile int waitStatus //节点清况


volatile Node prev //当前节点/系统任务管理器的前驱节点
volatile Node next; //当前节点/系统任务管理器的后继节点
volatile Thread thread;//加入同步队列的系统任务管理器引用
Node nextWaiter;//听候队列中的下另另一一四个节点

看多节点的数据价值形式,知道这是另另一一四个双向队列,而在AQS中还所处另另一一四个成员变量:

private transient volatile Node head;
private transient volatile Node tail;

AQS实际上通过头尾指针来管理同步队列,一起实现包括获取锁失败的系统任务管理器进行入队,释放锁时对同步队列中的系统任务管理器进行通知等核心最好的土办法。其示意图如下:

通过对源码的理解以及做实验的最好的土办法,现在朋友 儿都可不还可以清楚的知道之后几点:

  1. 节点的数据价值形式,即AQS的静态内控 类Node,节点的听候清况 等信息

  2. 同步队列是另另一一四个双向队列,AQS通过持有头尾指针管理同步队列

三、 ReentrantLock

重入锁ReentrantLock,顾名思义,统统支持重进入的锁,它表示该锁可不还可以支持另另一一四个系统任务管理器对资源的重复加锁。除此之外,该锁的还支持获取锁时的公平和非公平性确定。将会另另一一四个锁不支持可重入,那当另另一一四个系统任务管理器调用它的lock()最好的土办法获取锁之后,将会再次调用lock()最好的土办法,则该系统任务管理器将会被我每每个人所阻塞。

synchronized关键字隐式的支持重进入,比如另另一一四个synchronized修饰的递归最好的土办法,在最好的土办法执行时,执行系统任务管理器在获取了锁之后仍能连续多次地获得该锁。ReentrantLock嘴笨 好难像synchronized关键字一样支持隐式的重进入,而是在调用lock()最好的土办法时,将会获取到锁的系统任务管理器,可不还可以再次调用lock()最好的土办法获取锁而不被阻塞。

3.1 实现可重入性

重进入是指任意系统任务管理器在获取到锁之后可不还可以再次获取该锁而不必被锁所阻塞,该价值形式的实现需要处置以下另另一一四个哪此的问题图片。

  1. 系统任务管理器再次获取锁。锁需要去识别获取锁的系统任务管理器是有无为当前所处锁的系统任务管理器,将会是,则再次成功获取。

  2. 锁的最终释放。系统任务管理器重复n次获取了锁,而是在第n次释放该锁后,或多或少系统任务管理器可不还可以获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁将会成功释放。

ReentrantLock是通过组合自定义同步器来实现锁的获取与释放,以非公平性(默认的)实现为例

核心最好的土办法为nonfairTryAcquire:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //1. 将会该锁未被任何系统任务管理器占有,该锁能被当前系统任务管理器获取
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //2.若被占有,检查占有系统任务管理器是有无是当前系统任务管理器
    else if (current == getExclusiveOwnerThread()) {
        // 3. 再次获取,计数加一
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

该最好的土办法增加了再次获取同步清况 的处置逻辑:通过判断当前系统任务管理器是有无为获取锁的系统任务管理器来决定获取操作是有无成功,将会是获取锁的系统任务管理器再次请求,则将同步清况 值进行增加并返回true,表示获取同步清况 成功。成功获取锁的系统任务管理器再次获取锁,统统增加了同步清况 值,这也就要求ReentrantLock在释放同步清况 时减少同步清况 值。

protected final boolean tryRelease(int releases) {
    //1. 同步清况

减1
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        //2. 非要当同步清况

为0时,锁成功被释放,返回true
        free = true;
        setExclusiveOwnerThread(null);
    }
    // 3. 锁未被完全释放,返回false
    setState(c);
    return free;
}

将会该锁被获取了n次,没有前(n-1)次tryRelease(int releases)最好的土办法需要返回false,而非要同步清况 完全释放了,可不还可以返回true。都可不还可以看多,该最好的土办法将同步清况 是有无为0作为最终释放的条件,当同步清况 为0时,将占有系统任务管理器设置为null,并返回true,表示释放成功。

3.2 公平有无公平获取锁的区别

公平锁非公平锁何谓公平性,是针对获取锁而言的,将会另另一一四个锁是公平的,没有锁的获取顺序就应该符合请求上的绝对时间顺序,满足FIFO,ReentrantLock的构造最好的土办法无参时是构造非公平锁

public ReentrantLock() {
    sync = new NonfairSync();
}

另外还提供了另外四种 最好的土办法,可传入另另一一四个boolean值,true时为公平锁,false时为非公平锁

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

在顶端非公平锁获取时(nonfairTryAcquire最好的土办法)统统简单的获取了一下当前清况 做了或多或少逻辑处置,并没有考虑到当前同步队列中系统任务管理器听候的清况 。朋友 儿来看看公平锁的处置逻辑是要怎样的,核心最好的土办法为:

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

这段代码的逻辑与nonfairTryAcquire基本上总爱,唯一的不同在于增加了hasQueuedPredecessors的逻辑判断,最好的土办法名就可知道该最好的土办法用来判断当前节点在同步队列中是有无有前驱节点的判断,将会有前驱节点说明有系统任务管理器比当前系统任务管理器更早的请求资源,根据公平性,当前系统任务管理器请求资源失败。将会当前节点没有前驱节点句子,再才有做顶端的逻辑判断的必要性。公平锁每次都有从同步队列中的第另另一一四个节点获取到锁,而非公平性锁则不一定,有将会刚释放锁的系统任务管理器能再次获取到锁

公平锁 VS 非公平锁

  1. 公平锁每次获取到锁为同步队列中的第另另一一四个节点,保证请求资源时间上的绝对顺序,而非公平锁有将会刚释放锁的系统任务管理器下次继续获取该锁,则有将会因为或多或少系统任务管理器永远无法获取到锁,造成“饥饿”哪此的问题图片

  2. 公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。而是,ReentrantLock默认确定的是非公平锁,则是为了减少一次责上下文切换,保证了系统更大的吞吐量

四、 ReentrantReadWriteLock

之后学到的锁都有独占锁,哪此锁在同一时刻只允许另另一一四个系统任务管理器进行访问,而读写锁在同一时刻都可不还可以允或多或少个读系统任务管理器访问,而是在写系统任务管理器访问时,所有的读系统任务管理器和或多或少写系统任务管理器均被阻塞。读写锁维护了一对锁,另另一一四个读锁和另另一一四个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。

除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁可不还可以复杂性读写交互场景的编程最好的土办法。假设在系统任务管理器中定义另另一一四个共享的用作缓存数据价值形式,它大次责时间提供读服务(例如查询和搜索),而写操作占有的时间很少,而是写操作完成之后的更新需要对后续的读服务可见。

一般清况 下,读写锁的性能都有比排它锁好,将会大多数场景读是多于写的。在读多于写的清况 下,读写锁可不还可以提供比排它锁更好的并发性和吞吐量。Java并发包提供读写锁的实现是ReentrantReadWriteLock。

读写锁主要有以下另另一一四个价值形式:

  1. 公平性确定:支持非公平性(默认)和公平的锁获取最好的土办法,吞吐量还是非公平优于公平;

  2. 重入性:支持重入,读锁获取都可不还可以再次获取,写锁获取之后可不还可以再次获取写锁,一起可不上还可以获取读锁;

  3. 锁降级:遵循获取写锁,获取读锁再释放写锁的次序,写锁可不还可以降级成为读锁

4.1 读写锁的使用

ReadWriteLock仅定义了获取读锁和写锁的另另一一四个最好的土办法,即readLock()最好的土办法和writeLock()最好的土办法,而嘴笨 现——ReentrantReadWriteLock,除了接口最好的土办法之外,还提供了或多或少便于外界监控其内控 工作清况 的最好的土办法,主要有:

int getReadLockCount()//返回当前读锁被获取的次数。该次数不等于获取读锁的系统任务管理器数,将会另另一一四个系统任务管理器连续获取n次,没有返回的统统n
int getReadHoldCount()//返回当前系统任务管理器获取读锁的次数
boolean isWriteLocked()//判断写锁是有无被获取
int getWriteHoldCount()//返回当前写锁被获取的次数

读写锁使用:

public class Cache {
    static Map<String, Object> map = new HashMap<>();
    static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    static Lock r = reentrantReadWriteLock.readLock();
    static Lock w = reentrantReadWriteLock.writeLock();
    // 获取另另一一四个key对应的value
    public static final Object get(String key) {
        r.lock();
        try {
            return map.get(key);
        } finally {
            r.unlock();
        }
    }
    // 设置key对应的value,并返回旧的value
    public static final Object put(String key, Object value) {
        w.lock();
        try {
            return map.put(key, value);
        } finally {
            w.unlock();
        }
    }
    // 清空所有的内容
    public static final void clear() {
        w.lock();
        try {
            map.clear();
        } finally {
            w.unlock();
        }
    }
}

Cache组合另另一一四个非系统任务管理器安全的HashMap作为缓存的实现,一起使用读写锁的读锁和写锁来保证Cache是系统任务管理器安全的。在读操作get(String key)最好的土办法中,需要获取读锁,这使得并发访问该最好的土办法时不必被阻塞。写操作put(String key,Object value)最好的土办法和clear()最好的土办法,在更新HashMap需要要提前获取写锁,当获取写锁后,或多或少系统任务管理器对于读锁和写锁的获取均被阻塞,而非要写锁被释放之后,或多或少读写操作可不还可以继续。Cache使用读写锁提升读操作的并发性,也保证每次写操作对所有的读写操作的可见性,一起复杂性了编程最好的土办法。

4.2 实现原理

再分析下读写锁的实现原理,主要的内容包括:读写清况 的设计,写锁的获取与释放,读锁的获取与释放以及锁降级。

读写清况 的设计

读写锁同样依赖自定义同步器来实现同步功能,而读写清况 统统其同步器的同步清况 。回想ReentrantLock中自定义同步器的实现,同步清况 表示锁被另另一一四个系统任务管理器重复获取的次数,而读写锁的自定义同步器需要在同步清况 (另另一一四个整型变量)上维护多个读系统任务管理器和另另一一四个写系统任务管理器的清况 ,使得该清况 的设计成为读写锁实现的关键。

将会在另另一一四个整型变量上维护多种清况 ,就一定需要“按位切割使用”或多或少变量,读写锁将变量切分成了另另一一四个次责,高16位表示读,低16位表示写,如图:

写锁的获取与释放

写锁是另另一一四个支持重进入的排它锁。将会当前系统任务管理器将会获取了写锁,则增加写清况 。将会当前系统任务管理器在获取写锁时,读锁将会被获取(读清况 不为0)将会该系统任务管理器都有将会获取写锁的系统任务管理器,则当前系统任务管理器进入听候清况 :

protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    // 1. 获取写锁当前的同步清况


    int c = getState();
    // 2. 获取写锁获取的次数
    int w = exclusiveCount(c);
    if (c != 0) {
        // (Note: if c != 0 and w == 0 then shared count != 0)
        // 3.1 当读锁已被读系统任务管理器获取将会当前系统任务管理器都有将会获取写锁的系统任务管理器句子
        // 当前系统任务管理器获取写锁失败
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        // 3.2 当前系统任务管理器获取写锁,支持可重复加锁
        setState(c + acquires);
        return true;
    }
    // 3.3 写锁未被任何系统任务管理器获取,当前系统任务管理器可获取写锁
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}

写锁的释放与ReentrantLock的释放过程基本例如,每次释放均减少写清况 ,当写清况 为0时表示写锁已被释放,从而听候的读写系统任务管理器可不还可以继续访问读写锁,一起前次写系统任务管理器的修改对后续读写系统任务管理器可见。

protected final boolean tryRelease(int releases) {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    //1. 同步清况

减去写清况


    int nextc = getState() - releases;
    //2. 当前写清况

是有无为0,为0则释放写锁
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        setExclusiveOwnerThread(null);
    //3. 不为0则更新同步清况


    setState(nextc);
    return free;
}

读锁的获取与释放

读锁是另另一一四个支持重进入的共享锁,它可不还可以被多个系统任务管理器一起获取,在没有或多或少写系统任务管理器访问(将会写清况 为0)时,读锁总会被成功地获取,而所做的也统统(系统任务管理器安全的)增加读清况 。将会当前系统任务管理器将会获取了读锁,则增加读清况 。将会当前系统任务管理器在获取读锁时,写锁已被或多或少系统任务管理器获取,则进入听候清况 。另外将会要增加或多或少内控 功能,比如getReadHoldCount()最好的土办法,作用是返回当前系统任务管理器获取读锁的次数。读清况 是所有系统任务管理器获取读锁次数的总和,而每个系统任务管理器每每个人获取读锁的次数非要确定保所处ThreadLocal中,由系统任务管理器自身维护,这使获取读锁的实现变得复杂性。

protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
    //1. 将会写锁将会被获取而是获取写锁的系统任务管理器都有当前系统任务管理器句子,当前
    // 系统任务管理器获取读锁失败返回-1
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    int r = sharedCount(c);
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        //2. 当前系统任务管理器获取读锁
        compareAndSetState(c, c + SHARED_UNIT)) {
        //3. 下面的代码主统统新增的或多或少功能,比如getReadHoldCount()最好的土办法
        //返回当前获取读锁的次数
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            firstReaderHoldCount++;
        } else {
            HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    //4. 处置在第二步中CAS操作失败的自旋将会实现重入性
    return fullTryAcquireShared(current);
}

读锁的每次释放(系统任务管理器安全的,将会有多个读系统任务管理器一起释放读锁)均减少读清况 ,减少的 值是(1<<16)。

锁降级

锁降级指的是写锁降级成为读锁。将会当前系统任务管理器拥有写锁,而是将其释放,最后再获取读锁,或多或少分段完成的过程非要称之为锁降级。锁降级是指把持住(当前拥有的)写锁,再获取到读锁,而是释放(先前拥有的)写锁的过程。接下来看另另一一四个锁降级的示例。将会数据不常变化,统统多个系统任务管理器都可不还可以并发地进行数据处置,当数据变更后,将会当前系统任务管理器感知到数据变化,则进行数据的准备工作,一起或多或少处置系统任务管理器被阻塞,直到当前系统任务管理器完成数据的准备工作:

public void processData() {
readLock.lock();
if (!update) {
// 需要先释放读锁
readLock.unlock();
// 锁降级从写锁获取到开始英文英文
writeLock.lock();
try {
if (!update) {
// 准备数据的流程(略)
update = true;
}
readLock.lock();
} finally {
writeLock.unlock();
}
// 锁降级完成,写锁降级为读锁
}
try {
// 使用数据的流程(略)
} finally {
readLock.unlock();
}
}

当数据所处变更后,update变量(布尔类型且volatile修饰)被设置为false,此时所有访问processData()最好的土办法的系统任务管理器都可不还可以感知到变化,但非要另另一一四个系统任务管理器可不还可以获取到写锁,或多或少系统任务管理器会被阻塞在读锁和写锁的lock()最好的土办法上。当前系统任务管理器获取写锁完成数据准备之后,再获取读锁,而是释放写锁,完成锁降级。