文档章节

关于ReadWriteLock的一些使用

s
 snecker
发布于 2015/08/15 02:32
字数 1250
阅读 108
收藏 11

最近遇到一个业务场景

  1. 有三个耗时线程。
  2. 线程A、线程B相互独立,并且会周期性执行sql,通过时间 增量获取新数据
  3. 线程C也是增量获取数据,并且操作依赖于线程A和线程B产生的数据; 可以简单理解为:独立同步A表、B表,同步C表的时候需要依赖A、B表的数据,也可以把C表看成A、B表的关联关系表。

如何让这三条线程相互协作呢?

最开始的简单想法是:

1.直接把三条线程的方法体抽出来,合并成一个单线程执行,即先执行A,再执行B(或者2个线程执行A,B)之后执行C线程方法体。

问题:如果是耗时操作,那么有可能出现等到我执行C线程的时候,A或者B数据源有更新,当然C执行的时候是获取不到A或B的最新数据的,那样就有可能导致丢失有效关联关系。而且单线程也无法提升整体性能。

2.利用A、B数据源的最后更新时间,对比C表最后更新时间,取最小值,然后与上次更新时间形成时间差,再同步这个时间差内产生的数据。 缺点:需要准确维护最后更新日期,若丢失时间则需要重新计算

3.利用读写锁来控制同步。A、B线程写数据的时候用writeLock锁住,C中对A、B引用块加读锁,同步用writeLock.condition的 await()、signalAll()方法来控制

重点说下第三种 先让code来说话吧

package com.xx;

/**
 * Created on 2015/8/13.
 */

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestCondition2 {

    /**
     * 场景1:
     * 线程C开始同步,发现依赖的线程A或B数据不全,然后循环等待
     * 场景2:
     * A或B在同步的时候,禁止线程C同步。
     *
     * @param args
     */

    public static void main(String[] args) {
        ReadWriteLock orgReadWriteLock = new ReentrantReadWriteLock();//默认非公平锁
        Lock orgReadLock = orgReadWriteLock.readLock();
        Lock orgWriteLock = orgReadWriteLock.writeLock();
        Condition orgHasDataCond = orgReadWriteLock.writeLock().newCondition();//写锁支持condition,readLock().newCondition() throws UnsupportedOperationException.

        ReadWriteLock invRWLock = new ReentrantReadWriteLock();
        Lock invReadLock = invRWLock.readLock();
        Lock invWriteLock = invRWLock.writeLock();
        Condition invCondition = invWriteLock.newCondition();

        ExecutorService executor = Executors.newFixedThreadPool(4);
        Map mapA = new HashMap<>();
        Map mapB = new HashMap<>();
        Random r = new Random();
        //线程1:专门写A数据
        //线程2:专门写B数据,和A数据是独立的
        //线程3:依赖线程1和线程2
        //1
        Runnable r1 = () -> {
            while (true) {
                orgWriteLock.lock();
                try {
                    mapA.put("A", r.nextInt(10));
                    //signal
                    orgHasDataCond.signalAll();
                } finally {
                    orgWriteLock.unlock();
                }
                System.out.println("线程A等待2s。。。" + mapA.get("A"));
                try {
                    Thread.sleep(2000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //2
        Runnable r2 = () -> {
            while (true) {
                invWriteLock.lock();
                try {
                    mapB.put("B", r.nextInt(20));
                    invCondition.signalAll();
                } finally {
                    invWriteLock.unlock();
                }
                System.out.println("线程B等待1s。。。" + mapB.get("B"));
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Runnable r3 = () -> {
            while (true) {
                try {
                    //拿到线程A的值
                    Object a = null;
                    //依赖A
                    try {
                        orgReadLock.lock();
                        a = mapA.get("A");
                        System.out.println("线程3取到A的值:" + a);
                    } finally {
                        //unlock
                        orgReadLock.unlock();
                    }

                    try {
                        orgWriteLock.lock();
                        //线程3要求mapA小于5,否则一直等下去
                        while ((int) (a = mapA.get("A")) < 5) {
                            System.out.println("======线程3取到A的值小于5,等待。。。当前a=" + a);
                            orgHasDataCond.await();
                        }
                        System.out.println("【SUCCESSFUL】==========线程3终于取到了A:" + a);
                    } finally {
                        orgWriteLock.unlock();
                    }
                    //依赖B
                    Object b = null;
                    try {
                        invReadLock.lock();
                        b = mapB.get("B");
                        System.out.println("线程3取到B的值:" + b);
                    } finally {
                        //unlock
                        invReadLock.unlock();
                    }

                    try {
                        invWriteLock.lock();
                        //线程3要求mapA小于5,否则一直等下去
                        while ((int) (b = mapB.get("B")) < 15) {
                            System.out.println("======线程3取到B的值小于15,等待大于15。。。当前B=" + b);
                            invCondition.await();
                        }
                        System.out.println("【SUCCESSFUL】==========线程3终于取到了B:" + b);
                    } finally {
                        invWriteLock.unlock();
                    }
//                    orgReadLock.lock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //
        executor.execute(r1);
        executor.execute(r2);
        executor.execute(r3);
        executor.shutdown();
    }
}

运行结果

线程B等待1s。。。13
线程A等待2s。。。7
【SUCCESSFUL】==========线程3终于取到了A:7
线程3取到B的值:13
======线程3取到B的值小于15,等待大于15。。。当前B=13
======线程3取到B的值小于15,等待大于15。。。当前B=8
线程B等待1s。。。8
线程B等待1s。。。5
======线程3取到B的值小于15,等待大于15。。。当前B=5
线程A等待2s。。。1
线程B等待1s。。。3
======线程3取到B的值小于15,等待大于15。。。当前B=3
======线程3取到B的值小于15,等待大于15。。。当前B=1
线程B等待1s。。。1
线程A等待2s。。。4
线程B等待1s。。。7
======线程3取到B的值小于15,等待大于15。。。当前B=7
【SUCCESSFUL】==========线程3终于取到了B:15
线程B等待1s。。。15
线程A等待2s。。。5
线程B等待1s。。。1
线程B等待1s。。。7
线程A等待2s。。。1
线程B等待1s。。。17
线程B等待1s。。。16
线程A等待2s。。。4
线程3取到A的值:4
======线程3取到A的值小于5,等待。。。当前a=4

需要注意的是:condition如果和writelock绑定的话需要在writelock持有的情况下调用,否则抛出IllegalMonitorException。 引用官方一句

[IllegalMonitorException]Thrown to indicate that a thread has attempted to wait on an object's monitor or to notify other threads waiting on an object's monitor without owning the specified monitor.

© 著作权归作者所有

共有 人打赏支持
s
粉丝 1
博文 43
码字总数 10092
作品 0
长沙
技术主管
java线程:互斥锁与读写锁

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

_______-
2016/09/02
25
0
22、Java并发性和多线程-Java中的读/写锁

以下内容转自http://ifeve.com/read-write-locks/: 相比Java中的锁(Locks in Java)里Lock实现,读写锁更复杂一些。假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么...

easonjim
2017/06/17
0
0
第四章-java多线程核心技术-Lock锁-第三篇

Condition实现顺序运行 使用condition对象可以对线程业务进行规划和排序。请看如下代码 输出结果 读写锁的产生 ReentrantLock锁是完全互斥的锁,即同一时间内只有一个线程执行锁后面的方法,...

简心
09/06
0
0
java并发编程——锁机制

第一部分:synchronized和volatile 锁机制用来保护对象的一致性以及操作的原子性,是实现线程安全的重要手段。线程安全涉及到对象两个重要的状态:共享性和可变性。如果对象是不可变的、线程...

isam
2016/05/27
94
0
Java 多线程:Lock接口(接口方法分析,ReentrantLock,ReadWriteLock

前言 *** 当我们了解了多线程生成的原因之后,会有相应的解决办法,最典型的就是 synchronized 和 lock。lock可以说是 synchronized 的一个替代品,synchronized 能做的事,lock 基本都可以做...

icanos
2016/07/21
3
0

没有更多内容

加载失败,请刷新页面

加载更多

Mac OS 最强鼠标改键软件:BetterAndBetter

官网: http://www.better365.cn 话不多说,先上你们最喜欢的软件界面截图。 通用: 触摸板: 鼠标: 键盘: 情景模式: 文本跳窗(自动跳窗): 四角触发: 工具箱: 脚本: 关于: 说下我目...

故国有明
40分钟前
16
0
Hbase Schema 模型设计注意事项及示例

一、Hbase 数据模型概述 HBase的数据模型也是由表组成,每一张表里也有数据行和列,但是在HBase数据库中的行和列又和关系型数据库的稍有不同。 表(Table): HBase会将数据组织成一张表,表名必...

PeakFang-BOK
今天
6
0
Blockathon(2018)上海竞赛项目成果今天揭晓

开幕式现场 10月19日,Blockathon(2018)上海在黄浦区P2联合创业办公社举行,本次活动由50名区块链开发者组成9支参赛队伍,来自国内外优秀区块链开发团队的20名技术专家担任导师及裁判。9支队...

HiBlock
今天
6
0
微信小程序开发系列六:微信框架API的调用

微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发...

JerryWang_SAP
今天
12
0
5 个用 Python 编写 web 爬虫的方法

大家在读爬虫系列的帖子时常常问我怎样写出不阻塞的爬虫,这很难,但可行。通过实现一些小策略可以让你的网页爬虫活得更久。那么今天我就将和大家讨论这方面的话题。 我刚整理了一套2018最新...

糖宝lsh
今天
12
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部