并发编程系列之ReadWriteLock使用

Posted smileNicky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程系列之ReadWriteLock使用相关的知识,希望对你有一定的参考价值。

并发编程系列之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创建读锁和写锁

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();
    



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

Thread-0得到了读锁
Thread-1得到了读锁
Thread-1释放读锁
Thread-0释放读锁
Thread-3得到了写锁
Thread-3释放写锁
Thread-2得到了读锁
Thread-2释放读锁
Thread-4得到了写锁
Thread-4释放写锁
Thread-5得到了写锁
Thread-5释放写锁

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


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)
    



附录参考资料

以上是关于并发编程系列之ReadWriteLock使用的主要内容,如果未能解决你的问题,请参考以下文章

Java——多线程高并发系列之ReadWriteLock读写锁

Java——多线程高并发系列之ReadWriteLock读写锁

并发编程之ReadWriteLock读写锁

java并发编程之上下文切换等并发编程的挑战

Day836.ReadWriteLock -Java 并发编程实战

Day836.ReadWriteLock -Java 并发编程实战