JDK读写锁特性
如果我们要自己实现一把读写锁,我们就必须对读写锁有一定的认知:

  1. 读锁与写锁是互斥的,写锁与写锁是互斥的,但读锁与读锁是共享的。简单来说就是读锁是共享锁,写锁是独占锁也称之为互斥锁
  2. 读写锁中的写锁与我们的reentrantLock一样是独占锁,同样也是可重入锁,所以读写锁中的写锁具备了reentrantLock一样的特性,可重入并且重入得次数必须等于解锁次数才能解锁(重入次数<解锁次数则抛异常)
  3. 读写锁中读锁为共享锁,也就是多个线程可以共同读取同一条数据,但它同样是可重入得,也就是当读锁重入次数!=读锁解锁次数 一样会出现独占锁一样的情况就是读锁并不会释放。

    1. public static void main(String[] args) {
    2. ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    3. new Thread(() -> {
    4. lock.readLock().lock();
    5. System.out.println("读锁开始工作");
    6. LockSupport.parkNanos(1000000000*3L);
    7. System.out.println("读锁结束工作");
    8. lock.readLock().lock();
    9. lock.readLock().unlock();
    10. }).start();
    11. new Thread(() -> {
    12. lock.writeLock().lock();
    13. System.out.println("写锁开始工作");
    14. LockSupport.parkNanos(1000000000*3L);
    15. System.out.println("写锁结束工作");
    16. lock.writeLock().unlock();
    17. }).start();
    18. }
    19. 读锁开始工作
    20. 读锁结束工作
  4. 读写锁中的读锁解锁次数和其内部readCount有一定关系,就是必须是每一次解锁readCount-1,直至readCount=0我们才能称读锁被释放,简答来说就是众多读线程共同持有一把锁,只有每个线程执行完(释放锁)我们才能让下一个队列中的线程来抢锁。

    手写读写锁构思

    我们可以参照上一篇文章的手写reentrantLock来想想一下,我们是通过阻塞队列来装在挂起的线程,然后解锁时去unpark队列头部的线程,但是我们读写锁是要做到兼具共享与独占二者的特性,而我们通过reentrantLock事实上已经实现了独占锁,所以我们的难点就是如何在内部在实现一次共享锁就可以了。但是如何实现共享锁呢?
    我们参照jdk读写锁,我们可以写不同的方法来表明这是读锁的加锁解锁方法,所以会有tryLockShared()、lockShared()、tryUnlockShared()、unlockShared()四个方法来表明这是读锁。如果我们把线程区分为读线程和写线程,假设某个线程抢到了锁,则对应的readCount/writeCount 也会去+1。

    如何区分队列中的线程是读线程还是写线程?

    假设是我们写线程拿到了锁它结束后又该去唤醒下一个线程,那下一个线程是什么类型的线程呢?所以我们会封装一个内部类waitNode(源码是双向链表),然后每次lockShared抢锁时则会以waitNode的形式加入到阻塞队列以此来表示它属于读线程还是写线程。

    怎么一次唤醒多个读线程?

    假设我们首先拿到的是写线程,当它执行完后它要去唤醒队列中的下一个线程,但此时假设队列中存在三个线程依次是 读线程1、读线程2、写线程,但如果我们只唤醒一个线程这与我们的独占锁是没有区别的,所以必须是去唤醒两个读线程,所以我们需要在lockShared()方法里去唤醒队列中下一个读线程,也就是说其中一个读线程拿到读锁了,则会去唤醒下一个队列头部的读线程(如果头部不是则不会去唤醒),以此来达到循环唤醒的目的。

    手写读写锁代码

    ```java /**

    • @author heian
    • @create 2020-03-06-10:03 上午
    • @description 读写锁
    • 当共享锁占锁成功,独占锁无法抢锁成功;共享锁可以被多个线程占锁
    • 当独占锁占锁成功,共享锁无法占锁成功;独占锁无法被多个线程占锁
    • 不包含锁降级 */ public class MyReadWriteLock {

    //想获取锁的线程 private LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); //被引用的线程 private AtomicReference reference = new AtomicReference<>(); //计算重入得次数 private AtomicInteger readCount = new AtomicInteger(0);//共享锁 private AtomicInteger writeCount = new AtomicInteger(0);//独占锁

    //为了区分读线程还是写线程 class WaitNode{

    int type = 0;   //0:独占锁的线程  1:共享锁的线程
    Thread thread = null;
    
    public WaitNode(Thread thread, int type){
        this.thread = thread;
        this.type = type;
    }
    

    } /**

    • 尝试获取锁(不是真的去拿锁,所以不用加入到阻塞队列)
    • 修改count 和 reference acquire:重入的次数默认是1 */ public boolean tryLock(int acquires){ if (readCount.get() != 0){
       return false;
      
      } if (writeCount.get() != 0){
       //锁被占用(可能是自己)
       if (Thread.currentThread() == reference.get()){
           writeCount.set(writeCount.get()+acquires);//单线程  无需CAS
           return true;
       }
      
      }else {
       //非公平的实现
       if (writeCount.compareAndSet(writeCount.get(),writeCount.get()+1)){
           reference.set(Thread.currentThread());
           return true;
       }
      
      } return false; }

    /**

    • 抢锁(存在非公平现象)
    • 修改 queue */ public void lock(){ //锁被占用,则CAS自旋不断地去抢锁 int arg = 1; if (!tryLock(arg)){

       WaitNode readNode = new WaitNode(Thread.currentThread(),0);
       queue.offer(readNode);
       //lock 是不死不休所以得用for循环,既然CAS拿不到则由轻量级锁转为重量级锁(挂起阻塞)再一次去拿锁
       for (;;){
           WaitNode peek = queue.peek();
           //队列可能一个线程,所以offer进来的或者说唤醒进来的,都会去判断是不是头部,是头部则再一次去抢锁
           if (peek != null && peek.thread == Thread.currentThread()){
               if (tryLock(arg)){
                   queue.poll();
                   break;
               }else {
                   //可能一进来就是头部线程或者唤醒了非头部线程,挂起
                   LockSupport.park();
               }
           }else {
               //不是头部线程,在队列并挂起
               LockSupport.park();
           }
       }
      

      } }

    /**

    • 释放锁 返回布尔类型是为了仿照jdk实现
    • 修改 queue */ public boolean unlock(){ int arg = 1; if (tryUnlock(arg)){
       WaitNode peek = queue.peek();
       //存在队列为空可能,比如就一个抢锁的不会去加入到阻塞队列
       if (peek != null){
           LockSupport.unpark(peek.thread);
       }
       return true;
      
      } return false; }

    /**

    • 尝试去解锁
    • 解锁不用判断读锁占用情况:,修改count 和 reference */ public boolean tryUnlock(int releases){ if (reference.get() != Thread.currentThread()){
       throw new IllegalMonitorStateException("未能获取到锁,无法释放锁");
      
      }else {
       //只有是拿到锁的线程才有解锁的资格,所以此处是单线程
       int value = writeCount.get()- releases;
       writeCount.set(value);
       //当你lock多次,但是unlock一次,此时是不会释放锁,只是不阻塞罢了
       if (value == 0){
           reference.set(null);
           return true;
       }else {
           return false;
       }
      
      } }

    //读锁:独占锁 acquireShared(AQS中的模板方法) public void lockShared(){

    int arg = 1;
    //>0 抢锁成功 <0 失败
    if (tryLockShared(arg) <0){
        WaitNode writeNode = new WaitNode(Thread.currentThread(),1);
        queue.offer(writeNode);
        for (;;){
            WaitNode headNode = queue.peek();
            if (headNode != null && headNode.thread == Thread.currentThread()){
                //如果是头部线程,则再去抢锁,抢到了则移除,没抢到则挂起
                if (tryLockShared(arg)>0){
                    queue.poll();
                    //移除后再去拿下一个,如果下一个也是读锁,则将其唤醒
                    WaitNode peek = queue.peek();
                    if (peek != null && peek.type == 1){
                        System.out.println("读锁被唤醒或读锁唤醒下一个线程");
                        LockSupport.unpark(peek.thread);//唤醒下一个线程,下一个线程是挂起1、挂起2
                    }
                    break;
                }else {
                    LockSupport.park();//队列头部 挂起1
                }
            }else {
                LockSupport.park();//非队列头部 挂起2
            }
        }
    }
    

    }

    /**

    • (tryAcquireShared aqs方法)
    • 尝试获取读锁
    • 只修改count 因为reference是被多个线程引用
    • 这里返回int 是为了模拟jdk源码 */ public int tryLockShared(int acquires){ for (;;){
       if (writeCount.get() != 0 ){//&& Thread.currentThread() != reference.get()
           return -1;
       }else {
           int readNum = readCount.get();
           //多个读线程 CAS操作改变,可能会失败所以需要自旋for
           if (readCount.compareAndSet(readNum,readNum + acquires)){
               return 1;
           }
       }
      
      } }

    /**

    • releaseShared 这里返回布尔类型是为了仿照jdk实现
    • 解除共享锁 意味着所有的读锁都释放了 */ public boolean unlockShared(){ int arg = 1; if (tryUnlockShared(arg)){
       //读锁释放了,则去判断队列中有无写锁
       WaitNode peek = queue.peek();
       if (peek != null){
           System.out.println("读锁唤醒下一个线程");
           LockSupport.unpark(peek.thread);//读锁执行完去唤醒下一个线程 一定是写锁
       }
       return true;
      
      } return false; }

    /**

    • tryReleaseShared
    • 尝试解开读锁,就直接修改readCount值即可
    • 解锁不用判断写锁占用情况:读锁重入多次,解锁1次,则count就!=0,只有当读锁的count=0,才能表明读锁释放锁成功 */ public boolean tryUnlockShared(int releases){ //这里无需去唤醒读锁,因为这里你解锁也只能唤醒一个,而是应该放在lockShared方法里,全部唤醒处在头部的读锁 for (;;){
       int readNum = readCount.get();
       int readNum2 = readNum - releases;
       if (readCount.compareAndSet(readNum,readNum2)){
           return readNum2 == 0;
       }
      
      } }

}

测试准确性代码
```java
  public static void main(String[] args) {
        MyReadWriteLock lock = new MyReadWriteLock();

        new Thread(() -> {
            lock.lock();
            System.out.println("写锁开始工作");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("写锁结束工作");
            lock.unlock();
        }).start();
        new Thread(() -> {
            lock.lock();
            System.out.println("写锁开始工作");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("写锁结束工作");
            lock.unlock();
        }).start();

        new Thread(() -> {
            lock.lockShared();
            System.out.println("读锁开始工作");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("读锁结束工作");
            lock.unlockShared();
        }).start();
        new Thread(() -> {
            lock.lockShared();
            System.out.println("读锁开始工作");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("读锁结束工作");
            lock.unlockShared();
        }).start();
    }
写锁开始工作
写锁结束工作
写锁开始工作
写锁结束工作
读锁被唤醒或读锁唤醒下一个线程
读锁开始工作
读锁开始工作
读锁结束工作
读锁结束工作
  ok,至此完成手写读写锁的代码演示。但这个与我们今天要讲的AQS有什么关系呢?

引入AQS

  AQS:AbstractQuenedSynchronizer抽象的队列式同步器。是除了java自带的synchronized关键字之外的锁机制。**AQS说白了就是**一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。这个队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。

AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。用大白话来说,AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。实现了AQS的锁有:自旋锁、互斥锁、读锁写锁、条件产量、信号量、栅栏都是AQS的衍生物
看了上面的简单介绍是不是发现跟我们上述实现的基本一致,只是我这里没使用双向队列而是阻塞队列,还有就是它里面的这个state一个变量实际上它是以二进制的方式存储了两个count值,也就是上面我们的readCount和writeCount。另外AQS是抽象类,它帮我们实现了锁的分配机制,但由于我们的抢锁或者释放锁的机制各有差异,所以它采用模板设计模式帮我们实现了主要的四个方法:

    //方法一:类比我们的独占锁的lock(int arg)
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    //方法二:类比我们的独占锁的unlock(int arg)
     public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    //方法三:类比我们的独占锁的lockShared(int arg)
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    //方法四:类比我们的独占锁的unlockShared(int arg)
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
  可以发现上面四个方法很类似,但是它的tryAcquire(arg)、tryRelease(arg)、tryAcquireShared(arg)、tryReleaseShared(arg)都是留给开发者自己去实现的。这也就是模板设计模式,帮我们制定好了实现的框架,你只要往里面填充自己的实现方式即可。比如我要画一个ppt,这个ppt的开头已经固定好了使我们公司的logo和价值观,但是里面的内容就得有我们用户自己去实现。那我们就利用上面的读写锁来抽象出来一个我们的AQS。![屏幕快照 2020-03-08 下午2.21.04.png](https://cdn.nlark.com/yuque/0/2020/png/771792/1583648545548-b780e0b8-94a7-4353-a21b-0c9de56019ed.png#crop=0&crop=0&crop=1&crop=1&height=311&id=UilNN&name=%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202020-03-08%20%E4%B8%8B%E5%8D%882.21.04.png&originHeight=838&originWidth=1904&originalType=binary&ratio=1&rotation=0&showTitle=false&size=186710&status=done&style=none&title=&width=706)
    //方法一:类比我们的独占锁的trylock(int arg)
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
    //方法二:类比我们的独占锁的tryUnlock(int releases)
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
    //方法三:类比我们的独占锁的tryLockShared(int arg)
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }
    //方法四:类比我们的独占锁的tryUnlockShared(int releases)
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

AQS实现公平锁与非公平锁

第一步:抽象出自己的AQS类

/**
 * @author heian
 * @create 2020-03-05-8:21 下午
 * @description
 */
public class MyAQS {

    //想获取锁的线程
    protected LinkedBlockingQueue<WaitNode> queue = new LinkedBlockingQueue<>();
    //被引用的线程
    protected AtomicReference<Thread> reference = new AtomicReference<>();
    //计算重入得次数
    protected AtomicInteger readCount = new AtomicInteger(0);//共享锁
    protected AtomicInteger writeCount = new AtomicInteger(0);//独占锁

    //为了区分读线程还是写线程
    class WaitNode{
        int type = 0;   //0:独占锁的线程  1:共享锁的线程
        Thread thread = null;

        public WaitNode(Thread thread, int type){
            this.thread = thread;
            this.type = type;
        }
    }

    /**
     * 抢锁(存在非公平现象)
     * 修改 queue
     */
    public void lock(){
        //锁被占用,则CAS自旋不断地去抢锁
        int arg = 1;
        if (!tryLock(arg)){
            WaitNode readNode = new WaitNode(Thread.currentThread(),0);
            queue.offer(readNode);
            //lock 是不死不休所以得用for循环,既然CAS拿不到则由轻量级锁转为重量级锁(挂起阻塞)再一次去拿锁
            for (;;){
                WaitNode peek = queue.peek();
                //队列可能一个线程,所以offer进来的或者说唤醒进来的,都会去判断是不是头部,是头部则再一次去抢锁
                if (peek != null && peek.thread == Thread.currentThread()){
                    if (tryLock(arg)){
                        queue.poll();
                        break;
                    }else {
                        //可能一进来就是头部线程或者唤醒了非头部线程,挂起
                        LockSupport.park();
                    }
                }else {
                    //不是头部线程,在队列并挂起
                    LockSupport.park();
                }
            }

        }
    }

    /**
     * 释放锁 返回布尔类型是为了仿照jdk实现
     * 修改 queue
     */
    public boolean unlock(){
        int arg = 1;
        if (tryUnlock(arg)){
            WaitNode peek = queue.peek();
            //存在队列为空可能,比如就一个抢锁的不会去加入到阻塞队列
            if (peek != null){
                LockSupport.unpark(peek.thread);
            }
            return true;
        }
        return false;
    }

    //读锁:独占锁  tryAcquireShared(AQS中的模板方法)
    public void lockShared(){
        int arg = 1;
        //>0 抢锁成功 <0 失败
        if (tryLockShared(arg) <0){
            WaitNode writeNode = new WaitNode(Thread.currentThread(),1);
            queue.offer(writeNode);
            for (;;){
                WaitNode headNode = queue.peek();
                if (headNode != null && headNode.thread == Thread.currentThread()){
                    //如果是头部线程,则再去抢锁,抢到了则移除,没抢到则挂起
                    if (tryLockShared(arg)>0){
                        queue.poll();
                        //移除后再去拿下一个,如果下一个也是读锁,则将其唤醒
                        WaitNode peek = queue.peek();
                        if (peek != null && peek.type == 1){
                            System.out.println("读锁被唤醒或读锁唤醒下一个线程");
                            LockSupport.unpark(peek.thread);//唤醒下一个线程,下一个线程是挂起1、挂起2
                        }
                        break;
                    }else {
                        LockSupport.park();//队列头部 挂起1
                    }
                }else {
                    LockSupport.park();//非队列头部 挂起2
                }
            }
        }
    }

    /**
     * tryReleaseShared  这里返回布尔类型是为了仿照jdk实现
     * 解除共享锁 意味着所有的读锁都释放了
     */
    public boolean unlockShared(){
        int arg = 1;
        if (tryUnlockShared(arg)){
            //读锁释放了,则去判断队列中有无写锁
            WaitNode peek = queue.peek();
            if (peek != null){
                System.out.println("读锁唤醒下一个线程");
                LockSupport.unpark(peek.thread);//读锁执行完去唤醒下一个线程 一定是写锁
            }
            return true;
        }
        return false;
    }

    /**
     * 尝试获取锁(不是真的去拿锁,所以不用加入到阻塞队列)
     * 修改count 和 reference  acquire:重入的次数默认是1
     */
    public boolean tryLock(int acquires){
        throw new UnsupportedOperationException();
    }

    /**
     * 尝试去解锁
     * 解锁不用判断读锁占用情况:,修改count 和 reference
     */
    public boolean tryUnlock(int releases){
        throw new UnsupportedOperationException();
    }

    /**
     * acquireShared()
     * 尝试获取读锁
     * 只修改count 因为reference是被多个线程引用
     * 这里返回int 是为了模拟jdk源码
     */
    public int tryLockShared(int acquires){
        throw new UnsupportedOperationException();
    }

    /**
     * tryReleaseShared
     * 尝试解开读锁,就直接修改readCount值即可
     * 解锁不用判断写锁占用情况:读锁重入多次,解锁1次,则count就!=0,只有当读锁的count=0,才能表明读锁释放锁成功
     */
    public boolean tryUnlockShared(int releases){
        throw new UnsupportedOperationException();
    }

}

第二步:实现自的ReentrantLock

/**
 * @author heian
 * @create 2020-03-07-4:18 下午
 * @description 实现自己的AQS
 * 实现公平锁与非公平锁
 */
public class MyReentrantLock {

    private boolean isFair;
    private Sync sync;

    public MyReentrantLock(boolean isFair) {
        this.isFair = isFair;
        this.sync = new Sync();
    }
    public MyReentrantLock() {
        this.sync = new Sync();
    }


    //采取源码方式  内部类继承
    class Sync extends MyAQS{

        @Override
        public boolean tryLock(int acquires){
            return isFair == true ? tryFairLock(acquires):tryNonFairLock(acquires);
        };
        //非公平锁实现方式
        public boolean tryNonFairLock(int acquires){
            if (readCount.get() != 0){
                return false;
            }
            if (writeCount.get() != 0){
                //锁被占用(可能是自己)
                if (Thread.currentThread() == reference.get()){
                    writeCount.set(writeCount.get()+acquires);//单线程  无需CAS
                }
            }else {
                //非公平的实现
                if (writeCount.compareAndSet(writeCount.get(),writeCount.get()+1)){
                    reference.set(Thread.currentThread());
                    return true;
                }
            }
            return false;
        }
        //公平锁实现方式
        public boolean tryFairLock(int acquires)  {
            if (readCount.get() != 0){
                return false;
            }
            if (writeCount.get() != 0){
                //锁被占用(可能是自己)
                if (Thread.currentThread() == reference.get()){
                    writeCount.set(writeCount.get()+acquires);//单线程  无需CAS
                }
            }else {
                //为了实现公平锁,需要判断进来的线程是不是队列头部线程,不是则直接返回false(不让外来线程可乘之机)
                if (queue != null){
                    if (queue.peek().thread == Thread.currentThread()
                            && writeCount.compareAndSet(writeCount.get(),writeCount.get()+acquires)){
                        reference.set(Thread.currentThread());
                        return true;
                    }
                }else{
                    if (writeCount.compareAndSet(writeCount.get(),writeCount.get()+acquires)){
                        reference.set(Thread.currentThread());
                        return true;
                    }
                }

            }
            return false;
        }

        /**
         * 尝试去解锁
         * 解锁不用判断读锁占用情况:,修改count 和 reference
         */
        @Override
        public boolean tryUnlock(int releases){
            if (reference.get() != Thread.currentThread()){
                throw new IllegalMonitorStateException("未能获取到锁,无法释放锁");
            }else {
                //只有是拿到锁的线程才有解锁的资格,所以此处是单线程
                int value = writeCount.get()- releases;
                writeCount.set(value);
                //当你lock多次,但是unlock一次,此时是不会释放锁,只是不阻塞罢了
                if (value == 0){
                    reference.set(null);
                    return true;
                }else {
                    return false;
                }
            }
        }

    }

    public void lock(){
        sync.lock();
    }
    public boolean tryLock(){
        return sync.tryLock(1);
    }
    public void unlock(){
        sync.unlock();
    }

}

第三步:编写测试方法测试

public static void main(String[] args) {
        MyReentrantLock lock = new MyReentrantLock();

        new Thread(() -> {
            lock.lock();
            System.out.println("A开始");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("A结束");
            lock.unlock();
        }).start();

        new Thread(() -> {
            lock.lock();
            System.out.println("B开始");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("B结束");
            lock.unlock();
        }).start();

        new Thread(() -> {
            lock.lock();
            System.out.println("C开始");
            LockSupport.parkNanos(1000000000*1L);
            System.out.println("C结束");
            lock.unlock();
        }).start();
    }
A开始
A结束
B开始
B结束
C开始
C结束
  如果AQS中的变量不在同一个报下或者访问修饰符为private 导致你无法访问到MyAQS中的成员变量可使用反射拿到。比如:
MyAQS myAQS = new MyAQS();
Field queueField = myAQS.getClass().getDeclaredField("queue");
queueField.setAccessible(true);
LinkedBlockingQueue<MyAQS.WaitNode> queue = (LinkedBlockingQueue<MyAQS.WaitNode>) queueField.get(myAQS);

AQS实现读写锁

  在文章开头已经实现了读写锁,这里我们再利用抽象出来的AQS再来实现一次,看能否达到一样的效果。

第一步:编写测试方法测试

/**
 * @author heian
 * @create 2020-03-07-4:18 下午
 * @description
 */
public class MyReadWriteLock {

    private  boolean isFair;
    private Sync sync;

    public MyReadWriteLock(boolean isFair) {
        this.isFair = isFair;
        this.sync = new Sync();
    }
    public MyReadWriteLock() {
        this.sync = new Sync();
    }


    //采取源码方式  内部类继承
    class Sync extends MyAQS{
        @Override
        public boolean tryLock(int acquires){
            return isFair == true ? tryFairLock(acquires):tryNonFairLock(acquires);
        };
        //非公平锁实现方式
        public boolean tryNonFairLock(int acquires){
            if (readCount.get() != 0){
                return false;
            }
            if (writeCount.get() != 0){
                //锁被占用(可能是自己)
                if (Thread.currentThread() == reference.get()){
                    writeCount.set(writeCount.get()+acquires);//单线程  无需CAS
                }
            }else {
                //非公平的实现
                if (writeCount.compareAndSet(writeCount.get(),writeCount.get()+1)){
                    reference.set(Thread.currentThread());
                    return true;
                }
            }
            return false;
        }
        //公平锁实现方式
        public boolean tryFairLock(int acquires)  {
            if (readCount.get() != 0){
                return false;
            }
            if (writeCount.get() != 0){
                //锁被占用(可能是自己)
                if (Thread.currentThread() == reference.get()){
                    writeCount.set(writeCount.get()+acquires);//单线程  无需CAS
                }
            }else {
                //为了实现公平锁,需要判断进来的线程是不是队列头部线程,不是则直接返回false(不让外来线程可乘之机)
                if (queue != null){
                    if (queue.peek().thread == Thread.currentThread()
                            && writeCount.compareAndSet(writeCount.get(),writeCount.get()+acquires)){
                        reference.set(Thread.currentThread());
                        return true;
                    }
                }else{
                    if (writeCount.compareAndSet(writeCount.get(),writeCount.get()+acquires)){
                        reference.set(Thread.currentThread());
                        return true;
                    }
                }

            }
            return false;
        }

        /**
         * 尝试去解锁
         * 解锁不用判断读锁占用情况:,修改count 和 reference
         */
        @Override
        public boolean tryUnlock(int releases){
            if (reference.get() != Thread.currentThread()){
                throw new IllegalMonitorStateException("未能获取到锁,无法释放锁");
            }else {
                //只有是拿到锁的线程才有解锁的资格,所以此处是单线程
                int value = writeCount.get()- releases;
                writeCount.set(value);
                //当你lock多次,但是unlock一次,此时是不会释放锁,只是不阻塞罢了
                if (value == 0){
                    reference.set(null);
                    return true;
                }else {
                    return false;
                }
            }
        }

        /**
         * acquireShared()
         * 尝试获取读锁
         * 只修改count 因为reference是被多个线程引用
         * 这里返回int 是为了模拟jdk源码
         */
        @Override
        public int tryLockShared(int acquires){
            for (;;){
                if (writeCount.get() != 0 ){//&& Thread.currentThread() != reference.get()
                    return -1;
                }else {
                    int readNum = readCount.get();
                    //多个读线程 CAS操作改变,可能会失败所以需要自旋for
                    if (readCount.compareAndSet(readNum,readNum + acquires)){
                        return 1;
                    }
                }
            }
        }

        /**
         * tryReleaseShared
         * 尝试解开读锁,就直接修改readCount值即可
         * 解锁不用判断写锁占用情况:读锁重入多次,解锁1次,则count就!=0,只有当读锁的count=0,才能表明读锁释放锁成功
         */
        @Override
        public boolean tryUnlockShared(int releases){
            //这里无需去唤醒读锁,因为这里你解锁也只能唤醒一个,而是应该放在lockShared方法里,全部唤醒处在头部的读锁
            for (;;){
                int readNum = readCount.get();
                int readNum2 = readNum - releases;
                if (readCount.compareAndSet(readNum,readNum2)){
                    return readNum2 == 0;
                }
            }
        }

    }

    public void lock(){
        sync.lock();
    }

    public boolean unlock(){
        return sync.unlock();
    }

    public void lockShared(){
        sync.lockShared();
    }

    public boolean unlockShared() {
        return sync.unlockShared();
    }
}

第二步:编写测试方法测试

public static void main(String[] args) {
        MyReadWriteLock lock = new MyReadWriteLock();

        new Thread(() -> {
            lock.lock();
            System.out.println("写线程A开始");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("写线程A结束");
            lock.unlock();
        }).start();

        new Thread(() -> {
            lock.lock();
            System.out.println("写线程B开始");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("写线程B结束");
            lock.unlock();
        }).start();

        new Thread(() -> {
            lock.lockShared();
            System.out.println("读线程C开始");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("读线程C结束");
            lock.unlockShared();
        }).start();

        new Thread(() -> {
            lock.lockShared();
            System.out.println("读线程D开始");
            LockSupport.parkNanos(1000000000*3L);
            System.out.println("读线程D结束");
            lock.unlockShared();
        }).start();
    }
写线程A开始
写线程A结束
读线程C开始
读线程C结束
读锁唤醒下一个线程
写线程B开始
写线程B结束
读线程D开始
读线程D结束
  因为线程进入队列的顺序不一致所以这是我当时运行的情况。OK今天就写到这里,下一篇写下基于JDK的AQS方式实现信号量Semaphone、计数器CountDownLatch、回旋栅栏CyclicBarrier。