文档章节

关于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
长沙
技术主管
22、Java并发性和多线程-Java中的读/写锁

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

easonjim
2017/06/17
0
0
java线程:互斥锁与读写锁

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

_______-
2016/09/02
25
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
Java线程--锁(下)

在上文中提到了Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区分读写,称这种锁为普通锁。为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使...

beibugulf
2016/11/18
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

qduoj~前端~二次开发~打包docker镜像并上传到阿里云容器镜像仓库

上一篇文章https://my.oschina.net/finchxu/blog/1930017记录了怎么在本地修改前端,现在我要把我的修改添加到部署到本地的前端的docker容器中,然后打包这个容器成为一个本地镜像,然后把这...

虚拟世界的懒猫
32分钟前
0
0
UML中 的各种符号含义

Class Notation A class notation consists of three parts: Class Name The name of the class appears in the first partition. Class Attributes Attributes are shown in the second par......

hutaishi
43分钟前
0
0
20180818 上课截图

小丑鱼00
今天
1
0
Springsecurity之SecurityContextHolderStrategy

注:下面分析的版本是spring-security-4.2.x,源码的github地址是: https://github.com/spring-projects/spring-security/tree/4.2.x 先上一张图: 图1 SecurityContextHolderStrategy的三个......

汉斯-冯-拉特
今天
0
0
LNMP架构(Nginx负载均衡、ssl原理、生成ssl密钥对、Nginx配置ssl)

Nginx负载均衡 网站的访问量越来越大,服务器的服务模式也得进行相应的升级,比如分离出数据库服务器、分离出图片作为单独服务,这些是简单的数据的负载均衡,将压力分散到不同的机器上。有时...

蛋黄_Yolks
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部