ReadWriteLock使用及抢锁原理

概述

概念
  • 维护一对关联锁,一个只能用来读操作,一个只能用来写操作;

  • 读锁可以由多个读线程共同持有(共享锁),写锁是排他的(独享锁)。

  • 同一时间,两把锁不能被不同线程持有

使用场景

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

为什么读的时候还需要加锁?

当写的操作为需要保证原子性的多步操作时,此时如果只是在写上加锁而未在读上加锁,就可能会造成原子性问题,即当写执行到一半的时候,另外一个线程执行了读操作,从而使得读到的数据时不准确的,产生了数据分裂。

锁降级

指的是写锁降级为读锁。持有写锁的同时,在获取读锁,锁喉释放写锁的过程。写锁时线程独占,读锁是共享的,所以写到读是降级(读到写为升级,是不能实现的)。

应用

场景一
package com.hzw.subject1.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MyLockHashMap {
    private final Map<String, Object> m = new HashMap<>();

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

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

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

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

    public void clear() {
        w.lock();
        try {
            m.clear();
        } finally {
            w.unlock();
        }
    }
}
场景二
package com.hzw.subject1.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MyLockCache {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(UserInfoCache.get("insist"));
                }
            }).start();
        }
    }
}

class UserInfoCache {
    private static volatile boolean cacheValid;
    private static final ReadWriteLock rwl = new ReentrantReadWriteLock();

    static Object get(String dataKey) {
        Object data = null;
        //读取数据,加读锁
        rwl.readLock().lock();
        try {
            if (cacheValid) {
                data = Redis.data.get(dataKey);
            } else {
                //通过加锁的方式去访问DB,加写锁
                rwl.readLock().unlock();
                rwl.writeLock().lock();
                try {
                    if (!cacheValid) {
                        data = DataBase.queryUserInfo();
                        Redis.data.put(dataKey, data);
                        cacheValid = true;
                    } else {
                        data = Redis.data.get(dataKey);
                    }
                } finally {
                    //锁降级
                    rwl.readLock().lock();
                    rwl.writeLock().unlock();
                }
            }
            return data;
        } finally {
            rwl.readLock().unlock();
        }
    }
}

class DataBase {
    static String queryUserInfo() {
        System.out.println("查询数据库。。。");
        return "name:insist,age:18,gender:true,";
    }
}

class Redis {
    static Map<String, Object> data = new HashMap<>();
}

原理

流程

ReadWriteLock使用及抢锁原理_第1张图片

手写读写锁
package com.hzw.subject1.lock.mylock;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.*;

public class MyReadWriteLock implements ReadWriteLock {
    private AtomicInteger rCount = new AtomicInteger();
    private AtomicInteger wCount = new AtomicInteger();

    /**
     * 独占锁的拥有者
     */
    private AtomicReference<Thread> owner = new AtomicReference<>();
    /**
     * 等待队列
     */
    private LinkedBlockingQueue<Node> waiters = new LinkedBlockingQueue<>();
    static class Node{
        //独占锁type
        static final int MONOPOLY = 0;
        //共享锁type
        static final int SHARE = 1;
        /**
         * 约定必须为一下选项:
         *      MONOPOLY 为想获取独占锁的线程
         *      SHARE为想获取共享锁的线程
         */
        int type;
        Thread thread;
        int arg;

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

        public static Node createMonopoly(Thread thread) {
            return new Node(thread, MONOPOLY, 1);
        }

        public static Node createShare(Thread thread) {
            return new Node(thread, SHARE, 1);
        }
    }

    @Override
    public Lock readLock() {
        return new Lock() {
            @Override
            public void lock() {
                //尝试抢锁
                if (!tryLock()) {
                    Node current = Node.createMonopoly(Thread.currentThread());
                    //抢锁失败,加入等待队列
                    waiters.offer(current);
                    //自旋
                    for (; ; ) {
                        Node head = waiters.peek();
                        //如果唤醒的不是队列头,在判定为伪唤醒,让其重新进入park状态
                        if (null != head && head.thread == Thread.currentThread() && tryLock()) {
                            //唤醒的线程抢到锁以后移除等待队列
                            waiters.poll();
                            //如果下一个线程依旧是读线程,继续唤醒下一个线程
                            Node next = waiters.peek();
                            if (null != next && next.type == Node.SHARE) {
                                LockSupport.unpark(next.thread);
                            }
                            return;
                        }
                        LockSupport.park();
                    }
                }
            }

            @Override
            public boolean tryLock() {
                int wc = wCount.get();
                if (wc == 0)
                    return rCount.incrementAndGet() > 0;
                if (owner.get() == Thread.currentThread())
                    return rCount.incrementAndGet() > 0;
                return false;
            }

            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                //当前线程中断状态为true则抛出异常
                if (Thread.interrupted())
                    throw new InterruptedException();
                //尝试抢锁
                if (tryLock())
                    return true;
                //如果等待时间<=0则直接抢锁失败
                if (time <= 0)
                    return false;
                //将time转换为纳秒
                long nanos = unit.toNanos(time);
                //抢锁的最后期限
                long deadline = nanos + System.nanoTime();
                for (; ; ) {
                    if (tryLock())
                        return true;
                    if (System.nanoTime() - deadline > 0L)
                        return false;
                }
            }

            @Override
            public void unlock() {
                //尝试解锁
                if (tryUnlock()) {
                    //解锁成功,唤醒等待队列头部的线程
                    Node head = waiters.peek();
                    if (null != head) {
                        LockSupport.unpark(head.thread);
                    }
                }
            }


            /**
             * 尝试解除共享锁
             * @return 是否解除
             */
            private boolean tryUnlock() {
                for (; ; ) {
                    int rc = rCount.get();
                    int nrc = rc - 1;
                    if (rCount.compareAndSet(rc, nrc))
                        return nrc == 0;
                }
            }

            @Override
            public void lockInterruptibly() throws InterruptedException {
                //判断线程中断状态
                if (Thread.interrupted())
                    throw new InterruptedException();
                //尝试抢锁
                if (!tryLock()) {
                    Node current = Node.createMonopoly(Thread.currentThread());
                    //抢锁失败,加入等待队列
                    waiters.offer(current);
                    //自旋
                    for (; ; ) {
                        Node head = waiters.peek();
                        //如果唤醒的不是队列头,在判定为伪唤醒,让其重新进入park状态
                        if (null != head && head.thread == Thread.currentThread() && tryLock()) {
                            //唤醒的线程抢到锁以后移除等待队列
                            waiters.poll();
                            //如果下一个线程依旧是读线程,继续唤醒下一个线程
                            Node next = waiters.peek();
                            if (null != next && next.type == Node.SHARE) {
                                LockSupport.unpark(next.thread);
                            }
                            return;
                        }
                        //判断线程中断状态
                        if (Thread.interrupted())
                            throw new InterruptedException();
                        LockSupport.park();
                    }
                }
            }

            @Override
            public Condition newCondition() {
                return null;
            }
        };
    }

    @Override
    public Lock writeLock() {
        return new Lock() {
            @Override
            public void lock() {
                //尝试抢锁
                if (!tryLock()) {
                    Node current = Node.createMonopoly(Thread.currentThread());
                    //抢锁失败,加入等待队列
                    waiters.offer(current);
                    //自旋
                    for (; ; ) {
                        Node head = waiters.peek();
                        //如果唤醒的不是队列头,在判定为伪唤醒,让其重新进入park状态
                        if (current == head && tryLock()) {
                            //唤醒的线程抢到锁以后移除等待队列
                            waiters.poll();
                            return;
                        }
                        LockSupport.park();
                    }
                }
            }

            @Override
            public boolean tryLock() {
                int rc = rCount.get();
                int wc = wCount.get();
                //判断wCount是否为0,若wCount != 0 则说明锁已经被占用
                if (wc == 0) {
                    if (rc != 0)
                        return false;
                    //通过CAS(0, 1)进行抢锁
                    if (wCount.compareAndSet(wc, 1)) {
                        //将owner设为当前线程的引用
                        owner.set(Thread.currentThread());
                        return true;
                    }
                //判断当前占用锁的线程是否为当前线程
                } else if(owner.get() == Thread.currentThread()){
                    //锁重入,count++
                    wCount.getAndIncrement();
                    return true;
                }
                //锁已被占用,且不是自己或抢锁失败
                return false;
            }

            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                //当前线程中断状态为true则抛出异常
                if (Thread.interrupted())
                    throw new InterruptedException();
                //尝试抢锁
                if (tryLock())
                    return true;
                //如果等待时间<=0则直接抢锁失败
                if (time <= 0)
                    return false;
                //将time转换为纳秒
                long nanos = unit.toNanos(time);
                //抢锁的最后期限
                long deadline = nanos + System.nanoTime();
                for (; ; ) {
                    if (tryLock())
                        return true;
                    if (System.nanoTime() - deadline > 0L)
                        return false;
                }
            }

            @Override
            public void unlock() {
                //尝试解锁
                if (tryUnlock()) {
                    //解锁成功,唤醒等待队列头部的线程
                    Node head = waiters.peek();
                    if (null != head) {
                        LockSupport.unpark(head.thread);
                    }
                }
            }

            private boolean tryUnlock() {
                //判断是否是当前线程拿到的锁,如果不是,抛出异常
                if (owner.get() != Thread.currentThread())
                    throw new IllegalMonitorStateException();
                //将count减1
                int c = wCount.get() -1;
                //判断count是否为0,如果为0,则当前线程的锁全部解完,将owner置空
                if (c != 0)
                    return false;
                owner.set(null);
                wCount.set(c);
                return true;
            }


            @Override
            public void lockInterruptibly() throws InterruptedException {
                //判断线程中断状态
                if (Thread.interrupted())
                    throw new InterruptedException();
                //尝试抢锁
                if (!tryLock()) {
                    Node current = Node.createMonopoly(Thread.currentThread());
                    //抢锁失败,加入等待队列
                    waiters.offer(current);
                    //自旋
                    for (; ; ) {
                        Node head = waiters.peek();
                        //如果唤醒的不是队列头,在判定为伪唤醒,让其重新进入park状态
                        if (current == head && tryLock()) {
                            //唤醒的线程抢到锁以后移除等待队列
                            waiters.poll();
                            return;
                        }
                        //判断线程中断状态
                        if (Thread.interrupted())
                            throw new InterruptedException();
                        LockSupport.park();
                    }
                }
            }

            @Override
            public Condition newCondition() {
                return null;
            }
        };
    }
}

你可能感兴趣的:(Java)