极速快3_快3赢钱诀窍_极速快3赢钱诀窍 - 极速快3,快3赢钱诀窍,极速快3赢钱诀窍是全球最新科技资讯新闻专业发布平台,是非常全面的信息共享网站。

多线程与高并发(六) Lock

  • 时间:
  • 浏览:1

那我 学习了要怎样使用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()妙招上。当前任务管理器获取写锁完成数据准备那我 ,再获取读锁,你会释放写锁,完成锁降级。