多线程与高并发(六) Lock

  • 时间:
  • 浏览:10
  • 来源:幸运快3_快3彩金_幸运快3彩金

事先学习了怎么使用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()措施 上。当前多系统进程 获取写锁完成数据准备事先,再获取读锁,后来释放写锁,完成锁降级。