4.3.2 AQS

读写锁与AQS

ReadWriteLock

概念

维护一对关系锁,一个只能用于读操作,一个只能用于写操作
读锁可以由多个读线程同时持有,写锁是排他的。
**同一时间两把锁不能被不同线程持有**
	public class ReadWriteLock_Demo {
    //不希望读写的内容都不一样,需要加入锁机制
    volatile long i =0;

    Lock lock = new ReentrantLock();

    ReadWriteLock rwLock = new ReentrantReadWriteLock();

    public long read() {
        try {
            rwLock.readLock().lock();
            return  i;
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public void write() {
        rwLock.writeLock().lock();

        i++;

        rwLock.writeLock().unlock();
    }
//    public void read() {
//        lock.lock();
//
//        long a = i;
//
//        lock.unlock();
//    }
//
//    public void write() {
//        lock.lock();
//
//        i++;
//
//        lock.unlock();
//    }
    public static void main(String[] args) throws InterruptedException {
        final ReadWriteLock_Demo demo = new ReadWriteLock_Demo();

        for (int i=0;i<=30; i++){
            int n = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j=0; j<80000; j++){
                        if (n%3==0) demo.write();
                        else demo.read();
                    }
                }
            }).start();
        }

        Thread.sleep(10000);
        System.out.println(demo.read());
    }

}

适用场景

适合读取操作多于写入操作的场景,改进互斥锁的性能,
比如:集合的并发线程安全性改造、缓存组件

public class HashMap_Demo {

    private final Map<String,Object> map = new HashMap<>();

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final  Lock r = readWriteLock.readLock();
    private final  Lock w = readWriteLock.writeLock();

    public Object get(String key){
        r.lock();
        try {
            return map.get(key);
        }finally {
            r.unlock();
        }

    }

    public Object put(String key,Object obj){
        w.lock();
        try {
           return  map.put(key,obj);
        }finally {
            w.unlock();
        }
    }

    public Object[] allkeys(){
        r.lock();
        try {
            return  map.keySet().toArray();
        }finally {
          r.unlock();
        }
    }

    public void clear(){
        w.lock();
        try {
            map.clear();
        }finally {
            w.unlock();
        }

    }



}

锁降级

指的是写锁降级成为读锁,持有写锁的同时,在获取读锁,随后释放写锁的过程
写锁是线程独占,读锁是共享,所以写->读是降级.(读->写,是不能实现的)

实现最简单的读写锁

在这里插入图片描述
在这里插入图片描述

public class KaneReadWriteLock {
    volatile AtomicInteger readCount = new AtomicInteger(0);
    AtomicInteger writeCount = new AtomicInteger(0);

    //独占锁 拥有者
    AtomicReference<Thread> owner = new AtomicReference<>();

    //等待队列
    public volatile LinkedBlockingQueue<WaitNode> waiters = new LinkedBlockingQueue<WaitNode>();

    class WaitNode {
        int type = 0;   //0 为想获取独占锁的线程,  1为想获取共享锁的线程
        Thread thread = null;
        int arg = 0;

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


    //获取独占锁
    public void lock() {
        int arg = 1;
        //尝试获取独占锁,若成功,退出方法,    若失败...
        if (!tryLock(arg)) {
            //标记为独占锁
            WaitNode waitNode = new WaitNode(Thread.currentThread(), 0, arg);
            waiters.offer(waitNode);    //进入等待队列

            //循环尝试拿锁
            for (; ; ) {
                //若队列头部是当前线程
                WaitNode head = waiters.peek();
                if (head != null && head.thread == Thread.currentThread()) {
                    if (!tryLock(arg)) {      //再次尝试获取 独占锁
                        LockSupport.park();     //若失败,挂起线程
                    } else {     //若成功获取
                        waiters.poll();     //  将当前线程从队列头部移除
                        return;         //并退出方法
                    }
                } else {  //若不是队列头部元素
                    LockSupport.park();     //将当前线程挂起
                }
            }
        }
    }

    //释放独占锁
    public boolean unlock() {
        int arg = 1;

        //尝试释放独占锁 若失败返回true,若失败...
        if (tryUnlock(arg)) {
            WaitNode next = waiters.peek(); //取出队列头部的元素
            if (next != null) {
                Thread th = next.thread;
                LockSupport.unpark(th);     //唤醒队列头部的线程
            }
            return true;                //返回true
        }
        return false;
    }

    //尝试获取独占锁
    public boolean tryLock(int acquires) {
        //如果read count !=0 返回false
        if (readCount.get() != 0)
            return false;

        int wct = writeCount.get();     //拿到 独占锁 当前状态

        if (wct == 0) {
            if (writeCount.compareAndSet(wct, wct + acquires)) {     //通过修改state来抢锁
                owner.set(Thread.currentThread());  //  抢到锁后,直接修改owner为当前线程
                return true;
            }
        } else if (owner.get() == Thread.currentThread()) {
            writeCount.set(wct + acquires);     //修改count值
            return true;
        }

        return false;
    }

    //尝试释放独占锁
    public boolean tryUnlock(int releases) {
        //若当前线程没有 持有独占锁
        if (owner.get() != Thread.currentThread()) {
            throw new IllegalMonitorStateException();       //抛IllegalMonitorStateException
        }

        int wc = writeCount.get();
        int nextc = wc - releases;      //计算 独占锁剩余占用
        writeCount.set(nextc);      //不管是否完全释放,都更新count值

        if (nextc == 0) {  //是否完全释放
            owner.compareAndSet(Thread.currentThread(), null);
            return true;
        } else {
            return false;
        }

    }


    //获取共享锁
    public void lockShared() {
        int arg = 1;

        if (tryLockShared(arg) < 0) {    //如果tryAcquireShare失败
            //将当前进程放入队列
            WaitNode node = new WaitNode(Thread.currentThread(), 1, arg);
            waiters.offer(node);  //加入队列

            for (; ; ) {
                //若队列头部的元素是当前线程
                WaitNode head = waiters.peek();
                if (head != null && head.thread == Thread.currentThread()) {
                    if (tryLockShared(arg) >= 0) {    //尝试获取共享锁,  若成功
                        waiters.poll();      //将当前线程从队列中移除

                        WaitNode next = waiters.peek();
                        if (next != null && next.type == 1) {    //如果下一个线程也是等待共享锁
                            LockSupport.unpark(next.thread);    //将其唤醒
                        }
                        return;     //退出方法
                    } else {                      //若尝试失败
                        LockSupport.park();     //挂起线程
                    }
                } else {  //若不是头部元素
                    LockSupport.park();
                }

            }
        }
    }

    //解锁共享锁
    public boolean unLockShared() {
        int arg = 1;

        if (tryUnLockShared(arg)) {     //当read count变为0,才叫release share成功
            WaitNode next = waiters.peek();
            if (next != null) {
                LockSupport.unpark(next.thread);
            }
            return true;
        }
        return false;
    }

    //尝试获取共享锁
    public int tryLockShared(int acquires) {
        for (; ; ) {
            if (writeCount.get() != 0 &&
                    owner.get() != Thread.currentThread())
                return -1;

            int rct = readCount.get();
            if (readCount.compareAndSet(rct, rct + acquires)) {
                return 1;
            }
        }
    }

    //尝试解锁共享锁
    public boolean tryUnLockShared(int releases) {
        for (; ; ) {
            int rc = readCount.get();
            int nextc = rc - releases;
            if (readCount.compareAndSet(rc, nextc)) {
                return nextc == 0;
            }
        }
    }


}

模板方法模式

由于ReentrntLock和ReadWriteLock中存在大量的重复代码需要进行优化.
在这里插入图片描述
----此处有大量的代码后期补充上传到gitee

AQS抽象队列同步器-

AQS: ReentryLock, ReadWriteLock等有很多公共的部分, 被抽取出来放到AQS, 一些独特的地方由子类自己去实现
在这里插入图片描述

AbstractQueuedSynchronizer 是jdk提供的一个类
https://zhuanlan.zhihu.com/p/178991617
在这里插入图片描述
上面我们自己实现的AQS里面由两个count值, 一个readCount, 一个是writeCount, 但在jdk的AQS中只有一个state字段用来存储这两个count的值.

ReadWriteLock用一个int存储了两个count值

int =4byte=32 位 前16位为readCount 后16位 为writeCount
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值