文档章节

读写锁ReentrantReadWriteLock

石飞飞
 石飞飞
发布于 2017/04/19 15:28
字数 1019
阅读 5
收藏 1
  1. ReentrantReadWriteLock类
    • ReentrantReadWriteLock类位于java.util.concurrent.locks包中。
    • 读写锁,分为读锁和写锁,多个读锁不互斥,读锁和写锁互斥,写锁和写锁互斥,这一特性是由JVM控制的,你只要锁好相应的锁就好了。如果你的代码里只读数据,可以多人同时读,但不能同时写数据,那就上读锁好了;如果你的代码修改数据,只能一人在写,且不能同时读,那就上写锁。总之,读的时候上读锁,写的时候上写锁。

  2. 应用场景

  • 分别创建三个线程,三个线程用于写数据,三个线程用于读数据
  • public class ReadWriteLockTest {
        public static void main(String[] args) {
            //共享数据封装类
            final Queue queue = new Queue();
            
            for (int i = 0; i < 2; i++) {
                //创建3个线程不断从队列中获取数据
                new Thread(new Runnable() {
                    public void run() {
                        while (true) {
                            queue.get();
                        }
                    }
                }).start();
    
                //创建3个线程不断往队列中填充数据
                new Thread(new Runnable() {
                    public void run() {
                        while (true) {
                            queue.set(new Random().nextInt(10000));
                        }
                    }
                }).start();
    
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Queue {
        //共享数据,只能有一个线程能写改数据,但可以有多个线程同时读该数据
        private Object data = null;
    
        public void get() {
            System.out.println(Thread.currentThread().getName() + ",reading data start");
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ",reading data completed, data=" + data);
        }
    
        public void set(Object data) {
            System.out.println(Thread.currentThread().getName() + ",writing data start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.data = data;
            System.out.println(Thread.currentThread().getName() + ",writing data completed, data=" + data);
        }
    }

    打印结果:

             我看结果,线程Thread-0和Thread-2第一次读到的数据是由线程三写入的数据data=4986,这个没有问题,但是接下来线程Thread-0又读到了data=1763,线程Thread-2读到了data=1204,这个就不正常,线程Thread-0和Thread-2应该读的是同一份数据才对。可以看出线程在写的时候被读的线程打断,这是我们不愿意看到的,只有当全部写完了,我们才能去读。

  • 我们再看,使用ReentrantReadWriteLock来实现可以同时读,且读到的是同一份共享数据
  • public class ReadWriteLockTest {
        public static void main(String[] args) {
            //共享数据封装类
            final Queue queue = new Queue();
    
            for (int i = 0; i < 3; i++) {
                //创建3个线程不断从队列中获取数据
                new Thread(new Runnable() {
                    public void run() {
                        while (true) {
                            queue.get();
                        }
                    }
                }).start();
    
                //创建3个线程不断往队列中填充数据
                new Thread(new Runnable() {
                    public void run() {
                        while (true) {
                            queue.set(new Random().nextInt(10000));
                        }
                    }
                }).start();
            }
        }
    }
    
    class Queue {
        //共享数据,只能有一个线程能写改数据,但可以有多个线程同时读该数据
        private Object data = null;
        //读写锁
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
        public void get() {
            //上读锁
            lock.readLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + ",reading data start");
    
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ",reading data completed, data=" + data);
            } finally {
                lock.readLock().unlock();
            }
        }
    
        public void set(Object data) {
            //上写锁
            lock.writeLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + ",writing data start");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.data = data;
                System.out.println(Thread.currentThread().getName() + ",writing data completed, data=" + data);
            } finally {
                lock.writeLock().unlock();
            }
        }
    }

    打印结果:

           线程Thread-5写入的数据data=4915,线程Thread-0、Thread-2、Thread-4都读到了同一个份儿数据。

3.  利用读写锁模拟一个缓存实现:在实际项目开发中,我们会把经常查询的而且不经常变更的数据放到缓存redis中,读数据的时候直接去缓存中读,减小数据的压力。

/**
 * Created by shifeifei on 2017/4/19.
 * 读写锁模拟一个缓存方法
 */
public class Cache {

    //用Map模拟缓存,将数据库中查询到的数据存放到里面
    private Map<String, String> cache = new HashMap<String, String>();

    private ReadWriteLock lock = new ReentrantReadWriteLock();

    //当多个线程操作此方法读数据时,可能会出现线程安全问题
    private Object get(String key) {
        Object data = null;

        lock.readLock().lock();
        try {
            data = cache.get(key);
            if (null == data) {
                //这里给data赋值,相当于写数据,所以此时要打断读锁上写锁
                lock.readLock().unlock();
                lock.writeLock().lock();

                try {
                    //假如有三个线程同时要上写锁,第一个上锁成功,后面的线程执行到这里就不再赋值
                    if (null == data) {
                        data = "aaa";//这里的操作实际上是去数据库查询数据
                    }
                } finally {
                    lock.writeLock().unlock();
                }

                //写锁执行完成后在上读锁
                lock.readLock().lock();
            }
        } finally {
            lock.readLock().unlock();
        }
        return data;
    }
}

 

© 著作权归作者所有

共有 人打赏支持
石飞飞
粉丝 2
博文 64
码字总数 39883
作品 0
朝阳
程序员
私信 提问
ReentrantReadWriteLock读写锁及其在 RxCache 中的使用

一. ReentrantReadWriteLock读写锁 Lock 是相当于 synchronized 更面向对象的同步方式,ReentrantLock 是 Lock 的实现。 本文要介绍的 ReentrantReadWriteLock 跟 ReentrantLock 并没有直接的...

fengzhizi715
01/21
0
0
读写锁ReentrantReadWriteLock

锁是java并发同步的重要利器,比如volatile和synchronized等。这些都是排他性的,同一时刻允许一个线程跑。 但是有时候我们希望读多线程跑无阻塞,读写锁分开这样就能提高效率!很新运concurr...

is晓歌
2017/10/31
0
0
Java并发编程-读写锁(ReentrantReadWriteLock)

章节目录 ReentrantReadWriteLock 特性 读写锁接口示例 读写锁的实现分析 1. ReentrantReadWriteLock 特性 1.1 读写锁定义 1.2 读写锁使用场景 1.3 读写锁的优点 2.读写锁接口示例 如下为使用...

markfork
2018/05/27
0
0
java线程:互斥锁与读写锁

两种互斥锁机制: 1、synchronized 2、ReentrantLock ReentrantLock是jdk5的新特性,采用ReentrantLock可以完全替代替换synchronized传统的锁机制,而且采用ReentrantLock的方式更加面向对象...

_______-
2016/09/02
25
0
Java并发系列4--读写锁ReadWriteLock

今天讲另一个并发工具,叫读写锁。读写锁是一种分离锁,是锁应用中的一种优化手段。 考虑读多写少的情况,这时如果我们用synchronized或ReentrantLock直接修饰读/写方法未尝不可,如: 简单有...

大大枣
2018/06/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周五乱弹 —— 看来我只适合当一个千斤顶

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @小小编辑:推荐歌曲《我想我是伟大的》 《我想我是伟大的》- 九天 手机党少年们想听歌,请使劲儿戳(这里) 最近面试的好多呀, 面试了一个漂...

小小编辑
21分钟前
194
7
grafana安装使用及与zabbix集成

grafana简介 Grafana是一个完全开源的度量分析与可视化平台,可对来自各种各种数据源的数据进行查询、分析、可视化处理以及配置告警。 Grafana支持的数据源: 官方:Graphite,InfluxDB,Ope...

阿dai学长
54分钟前
12
0
带你看数据挖掘与机器学习-厦大EDP上课出勤预测

带你看数据挖掘与机器学习-厦大EDP上课出勤预测 标签: 数据挖掘 特征工程 机器学习 出勤预测 write by xmhexi 2019/3/22 内容提要 首先说明本文是一篇科普文章,通过一个实际案例,帮助理解什...

xmhexi
今天
128
0
IOS  学习记录

1.StackView=>IOS 9及以上支持 2.布局方式: AutoLayout / StackView 堆布局 (线性布局) 3.屏幕适配 (资源分辨率、设计分辨率、屏幕分辨率) Size Class技术 可以针对 屏幕的方向进行设置...

萨x姆
今天
5
0
第四次工业革命:自主经济的崛起

https://36kr.com/p/5170370.html

shengjuntu
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部