并发编程系列之ReadWriteLock使用

2021-12-04 10:30:56 浏览数 (1)

并发编程系列之ReadWriteLock使用

1、什么是ReadWriteLock?

ReadWriteLock是jdk的juc包中提供的读写锁api,维护一对关联的读锁、写锁,读锁可以被多个读线程共享,写锁排他。

2、为什么需要ReadWriteLock?

之前我们喜欢使用ReentrantLock,重入锁,既然提供了ReentrantLock这个api,jdk官方又推出ReadWriteLock,相对ReentrantLock来说,ReadWriteLock是比较省资源的,ReentrantLock虽然也可以保证线程安全,但是比较耗资源的,比如在所有线程都是读锁的情况,这种情况就是线程安全的,就不需要做线程安全控制,直接保证线程并行执行就行,但是ReentrantLock不能做到,所以ReadWriteLock根据一系列规则保证了线程安全也保证了执行效率。

3、ReadWriteLock的常用API

ReadWriteLock在jdk8的juc包里有两个实现类:

ReentrantReadWriteLock的主要方法:

4、读写锁的获取规则

  1. 如果一个线程已经占用了读锁,其他线程想要申请读锁,是可以申请成功的
  2. 如果一个线程已经占用了读锁,其他线程想要申请写锁,是不可以申请成功的
  3. 如果一个线程已经占用了写锁,其他线程想要申请读锁还是写锁,都是不可以申请成功的

总结起来就是:读读共享、其他都互斥(写写互斥、读写互斥、写读互斥)

5、ReadWriteLock的适用场景

知道了ReadWriteLock的特效之后,我们知道相比于 ReentrantLock 适用于一般场合,ReadWriteLock 适用于读多写少的情况,合理使用可以进一步提高并发效率

6、ReadWriteLock的例子

例子,使用ReentrantReadWriteLock创建读锁和写锁

代码语言:javascript复制
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {

    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
    private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

    public static void read() {
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() "得到了读锁");
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() "释放读锁");
            readLock.unlock();
        }
    }

    public static void write() {
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName()   "得到了写锁");
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName()  "释放写锁");
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        new Thread(() -> read()).start();
        new Thread(() -> read()).start();
        new Thread(() -> read()).start();
        new Thread(() -> write()).start();
        new Thread(() -> write()).start();
        new Thread(() -> write()).start();
    }

}

控制台打印,从打印结果可以看出,读锁可以同时被多个线程获得的,一个线程获取到读锁之后,另外一个线程又获取来了,而写锁必须等线程释放之后,才可以被其他线程获取,所以读锁共享,写锁排他

代码语言:javascript复制
Thread-0得到了读锁
Thread-1得到了读锁
Thread-1释放读锁
Thread-0释放读锁
Thread-3得到了写锁
Thread-3释放写锁
Thread-2得到了读锁
Thread-2释放读锁
Thread-4得到了写锁
Thread-4释放写锁
Thread-5得到了写锁
Thread-5释放写锁

锁降级:指的是写锁降级成为读锁。把持住当前写锁的同时,再获取读锁,随后释放写锁的过程。写锁是线程独占的,读锁是共享的。所以写锁降级为读锁是降级。读锁升级为写锁不能实现。例子来自网上,稍作修改:

代码语言:javascript复制
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockCachedDataExample {

    class Data {}
    class RWDictionary {
        private final Map<String, Data> map = new ConcurrentHashMap<>();
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private final Lock rLock = readWriteLock.readLock();
        private final Lock wLock = readWriteLock.writeLock();
        public Data get(String key) {
            rLock.lock();
            try {
                return map.get(key);
            }finally {
                rLock.unlock();
            }
        }
        public Data put(String key , Data value) {
            wLock.lock();
            try {
                return map.put(key , value);
            }finally {
                wLock.unlock();
            }
        }
        public String[] allKeys() {
            rLock.lock();
            try {
                return (String[]) map.keySet().toArray();
            }
            finally {
                rLock.unlock();
            }
        }
        public void clear() {
            wLock.lock();
            try {
                map.clear();
            }
            finally {
                wLock.unlock();
            }
        }
    }
    class CachedData {
        Object data;
        volatile boolean cacheValid;
        final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

        void processCachedData() {
            rwl.readLock().lock();
            if (!cacheValid) {
                // Must release read lock before acquiring write lock
                rwl.readLock().unlock();
                rwl.writeLock().lock();
                try {
                    // Recheck state because another thread might have
                    // acquired write lock and changed state before we did.
                    if (!cacheValid) {
                        data = getData();
                        cacheValid = true;
                    }
                    // 【降级】Downgrade by acquiring read lock before releasing write lock
                    rwl.readLock().lock();
                }finally {
                    rwl.writeLock().unlock();
                }
            }

            try {
                use(data);
            } finally {
                rwl.readLock().unlock();
            }
        }

        private Object getData() {
            return null;
        }

        void use(Object data){}
    }

}

附录参考资料

  • http://tutorials.jenkov.com/java-concurrency/read-write-locks.html
  • https://www.tutorialspoint.com/java_concurrency/concurrency_readwritelock.htm

0 人点赞