美文网首页
JUC-(8)ReentrantReadWriteLock

JUC-(8)ReentrantReadWriteLock

作者: 一个菜鸟JAVA | 来源:发表于2020-07-17 14:38 被阅读0次

    ReentrantReadWriteLock是一个读写锁,与ReentrantLock不同之处在于它提供两种模式的锁,一种为读锁另一种为写锁.读锁是一种共享锁,即当一个线程已经拥有了读锁,其他线程仍然可以继续请求获取读锁,但是不能获取写锁.写锁则是一种排他锁,即当一个线程已经拥有了写锁,那么其他线程就无法获取读锁或者写锁.
    例如我们应用中的缓存,允许多个线程同时读取数据,但是对于写入数据的情况就只能允许一个线程进行.下面我们使用代码简单的实现一个缓存.

    public class App9 {
        public static void main(String[] args) throws InterruptedException {
            Cache cache = new Cache();
            //更新缓存线程
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+":写入缓存");
                    cache.put("info","测试内容");
                }
            });
            t1.setName("t1");
            t1.start();
    
            Thread.sleep(500);
    
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    String info = cache.get("info");
                    System.out.println(Thread.currentThread().getName() + ":" + info);
                }
            };
    
            Thread t2 = new Thread(runnable);
            t2.setName("t2");
            t2.start();
    
            Thread t3 = new Thread(runnable);
            t3.setName("t3");
            t3.start();
    
        }
    }
    
    class Cache{
        //读写锁
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        //存储缓存数据
        private Map<String,String> map = new HashMap<>();
    
        /**
         * 存入数据
         * @param key
         * @param value
         */
        public void put(String key,String value){
            //添加写锁
            lock.writeLock().lock();
            //放入数据
            map.put(key,value);
            //2s之后再释放锁
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //解锁
            lock.writeLock().unlock();
        }
    
        /**
         * 获取数据
         * @param key
         * @return
         */
        public String get(String key){
            //获取读锁
            lock.readLock().lock();
            //获取数据
            String value = map.get(key);
            //2s之后再释放锁
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.readLock().unlock();
            return value;
        }
    }
    

    执行结果如下:

    t1:写入缓存
    t2:测试内容
    t3:测试内容
    

    上面的代码中,我们让写入缓存线程t1先获取锁,而读取缓存的线程t2和她t3等待写锁释放.当写锁释放之后,读线程t2和t3立马都打印出缓存数据.由此可以说明,写锁再被当前线程获取之后,其他线程都无法再次获取到读锁或者写锁.而读锁被获取后,其他线程是可以获取到写锁的.
    除此之外,ReentrantReadWriteLock还有以下几个特征:

    • 可重入性:这个跟ReentrantLock一样也支持重入.即当前线程已获得锁,接下来它可以再次获取锁.
    • 锁降级:它在支持重入的情况下,还支持从写锁降级到读锁.但是需要注意的是读锁是无法降级成读锁的.
    public class App10 {
        public static void main(String[] args) {
            ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    //获取写锁
                    lock.writeLock().lock();
                    System.out.println("获取写锁");
                    //获取读锁
                    lock.readLock().lock();
                    System.out.println("获取读锁");
                    //释放读锁
                    lock.readLock().unlock();
                    System.out.println("释放读锁");
                    //释放写锁
                    lock.writeLock().unlock();
                    System.out.println("释放写锁");
                }
            });
            t1.setName("t1");
            t1.start();
        }
    }
    

    上面就是锁降级的例子.在上面的例子中,线程先获取写锁,然后再获取读锁.最后程序能顺利打印出结果并结束.如果先获取读锁再获取写锁,这时候线程在获取写锁的时候会阻塞永远无法获取到写锁.

    同样读写锁也支持Condition,读写锁除了锁区分读锁和写锁之外,其他功能跟ReentrantLock基本上就类似了.如果还不了解ReentrantLock如何使用,可以看我之前写的相关文章.

    相关文章

      网友评论

          本文标题:JUC-(8)ReentrantReadWriteLock

          本文链接:https://www.haomeiwen.com/subject/viughktx.html