读写锁的使用

本文内容

何时该使用读写锁.
读写锁的写法.
理解读写锁和线程互斥的区别。

复习-同步化的概念

当一个方法或代码块被声明成synchronized,要执行此代码必须先取得一个对象实例或this的锁定,这个锁定要在 synchronized修饰的方法或代码块执行完后才能释放掉(无论这段代码是怎样返回的,是正常运行还是异常运行)。每个对象只有一个锁定,如果有两 个不同的线程试图同时调用同一对象的同步方法,最终只会有一个能运行此方法,另外一个要等待第一个线程释放掉锁定后才能运行此方法。

读写锁应用的场合

我们有时会遇到对同一个内存区域如数组或者链表进行多线程读写的情况,一般来说有以下几种处理方式: 1.不加任何限制,多见于读取写入都很快的情况,但有时也会出现问题. 2.对读写函数都加以同步互斥,这下问题是没了,但效率也下去了,比如说两个读取线程不是非要排队进入不可. 3.使用读写锁,安全和效率都得到了解决,特别合适读线程多于写线程的情况.也就是下面将要展现的模式.

读写锁的意图

读写锁的本意是分别对读写状态进行互斥区分,有互斥时才加锁,否则放行.互斥的情况有: 1.读写互斥. 2.写写互斥. 不互斥的情况是:读读,这种情况不该加以限制. 程序就是要让锁对象知道当前读写状态,再根据情况对读写的线程进行锁定和解锁。

读写线程都要操作的数据类

读写线程都要操作的数据是链表datas。
注意其中try...finally 的写法,它保证了加锁解锁过程是成对调用的

l public   class   DataLib   {
     private List<String> datas;
 
     private ReadWriteLock lock;
 
       public   DataLib()   {
         datas = new ArrayList<String>();
         lock = new ReadWriteLock();
       }
 
       //   写入数据 , 这时不能读取
       public   void   writeData(List<String>   newDatas)   {
           try   {
             lock.writeLock();
             Test.sleep(2);
             datas=newDatas;
           }   finally   {
             lock.writeUnlock();
           }
       }
 
       //   读取数据 , 这时不能写入
       public   List<String>   readData()   {
           try   {
             lock.readLock();
               Test.sleep(1);             
             return datas;
           }   finally   {
             lock.readUnlock();
           }
 
       }
 
 }

读写锁ReadWriteLock类

public class ReadWriteLock{
    // 读状态
    private boolean isRead;
   
    // 写状态
    private boolean isWrite;
   
    public synchronized void readLock(){
        // 有写入时读取线程停止
        while(isWrite){
            try{   
                System.out.println("有线程在进行写入,读取线程停止,进入等待状态");
                wait();
            }
            catch(InterruptedException ex){
                ex.printStackTrace();
            }
        }
       
        System.out.println("设定锁为读取状态");
        isRead=true;
    }
   
    public synchronized void readUnlock(){
        System.out.println("解除读取锁");
        isRead=false;
        notifyAll();
    }

public synchronized void writeLock(){
        // 有读取时读取线程停止
        while(isRead){
            try{   
                System.out.println("有线程在进行读取,写入线程停止,进入等待状态");
                wait();
            }
            catch(InterruptedException ex){
                ex.printStackTrace();
            }
        }
       
        // 有写入时写入线程也一样要停止
        while(isWrite){
            try{   
                System.out.println("有线程在进行写入,写入线程停止,进入等待状态");
                wait();
            }
            catch(InterruptedException ex){
                ex.printStackTrace();
            }
        }
       
        System.out.println("设定锁为写入状态");
        isWrite=true;
    }
   
    public synchronized void writeUnlock(){
        System.out.println("解除写入锁");
        isWrite=false;
        notifyAll();
    }
}

写线程类Writer -它用于往DataLib类实例中的datas字段写数据

分析其中dataLib字段的用意。
注意并记住其中持续调用及使用随机数的方法。

l public class Writer implements Runnable{
     private DataLib dataLib;
     private static final Random random=new Random();
       private   String[]   mockDatas={" 甲 "," 乙 "," 丙 "," 丁 "," 戊 "," 己 "," 庚 "," 辛 "," 壬 "," 癸 "};     
      
     public Writer(DataLib dataLib,String[] mockDatas){
         this.dataLib=dataLib;
         this.mockDatas=mockDatas;
          
         Thread thread=new Thread(this);
         thread.start();
       }
      
     public void run(){
         while(true){
             Test.sleep(random.nextInt(3));
              
             int startIndex=random.nextInt(mockDatas.length);
              
             ArrayList<String> newDatas=new ArrayList<String>();
             for(int i=startIndex;i<mockDatas.length;i++){
                 newDatas.add(mockDatas[i]);
               }
              
             dataLib.writeData(newDatas);
           }
       }
 }

读线程类Reader  -它用于从DataLib类实例中的datas字段读取数据

分析其中dataLib字段的用意。
注意并记住其中持续调用及使用随机数的方法。

public class Reader implements Runnable{
    private DataLib dataLib;
    private static final Random random=new Random();
   
    public Reader(DataLib dataLib){
        this.dataLib=dataLib;
   
        Thread thread=new Thread(this);
        thread.start();
    }
   
    public void run(){
        while(true){
            Test.sleep(random.nextInt(2));           
            List<String> datas=dataLib.readData();
           
            System.out.print(">>取得数组为:");
            for(String data:datas){
                System.out.print(data+",");
            }
            System.out.print("/n");
        }
    }
}

将代码运行起来

右边的代码创建了两个写线程和三个读线程,它们都是对dataLib实例进行操作的。
五个线程都有一个dataLib字段,都提供了一个带参构造函数以给datas字段赋值,这就保证了五个线程操作的都是一个实例的同一字段,也就是同一片内存。
读写锁就是对这五个线程进行控制的。
当有一个读线程在操作时,其它的写线程无法进行操作,读线程可以正常操作,互不干扰。
当有一个写线程在操作时,其它的读线程无法进行操作。

 public class Test{
     public static void main(String[] args){
         DataLib dataLib=new DataLib();
          
           String[]   mockDatas1={" 甲 "," 乙 "," 丙 "," 丁 "," 戊 "," 己 "," 庚 "," 辛 "," 壬 "," 癸 "};
         Writer writer1=new Writer(dataLib,mockDatas1);
          
           String[]   mockDatas2={" 子 "," 丑 "," 寅 "," 卯 "," 辰 "," 巳 "," 午 "," 未 "," 申 "," 酉 "," 戌 "," 亥 "};
         Writer writer2=new Writer(dataLib,mockDatas2);
          
         Reader reader1=new Reader(dataLib);
         Reader reader2=new Reader(dataLib);
         Reader reader3=new Reader(dataLib);
       }
      
      
       //   用于延时
     public static void sleep(int sleepSecond){
         try{
             Thread.sleep(sleepSecond*1000);
           }
         catch(Exception ex){
             ex.printStackTrace();
           }
       }
 }

小结

当多个线程试图对同一内容进行读写操作时适合使用读写锁。
请理解并记住ReadWriteLock类读写锁的写法.
读写锁相对于线程互斥的优势在于高效,它不会对两个读线程进行盲目的互斥处理,当读线程数量多于写线程尤其如此,当全是写线程时两者等效。

Android 配置文件锁设置相关推荐

  1. Android Studio简单设置

    2019独角兽企业重金招聘Python工程师标准>>> Android Studio 简单设置 界面设置 默认的 Android Studio 为灰色界面,可以选择使用炫酷的黑色界面 ...

  2. Android NDK 编译选项设置[zhuan]

    http://crash.163.com/#news/!newsId=24 在Android NDK开发中,有两个重要的文件:Android.mk和Application.mk,各尽其责,指导编译器如 ...

  3. Android EditText的设置

    1.输入法Enter键图标的设置: 软件盘的界面替换只有一个属性android:imeOptions,这个属性的可以取的值有normal,actionUnspecified,actionNone,ac ...

  4. 【转】Android Studio简单设置

    原文网址:http://ask.android-studio.org/?/article/14 Android Studio 简单设置 界面设置 默认的 Android Studio 为灰色界面,可以 ...

  5. android+自定义版本号,Android打包版本号设置方法

    之前没有设置过打包的命名,每次打包都是默认的"app-realease.apk",之后手动修改名字来显示出它是一个新版本. 晚上学习了如何配置打包名称,很简单,修改build.gr ...

  6. Android 各版本 设置 USB 默认连接 MTP 模式 ( Android 6.0+ )

    Android 各版本 设置 USB 默认连接 MTP 模式 ( Android  6.0+ ) Android 6.0 以及之后的版本,google默认设计直接配置USB连接模式为 :仅充电: 项目 ...

  7. Android开发技巧——设置系统状态栏颜色

    开门见山,先来三张效果图: 然后我们再来讲如何实现以及如何快速地实现. 如何实现 实现设置系统状态栏颜色需要至少在Android 4.4.2(API 19)以上.这是因为,在这个版本以下,没有任何的A ...

  8. Android APN的设置问题

    Android APN的设置问题 推荐 原创yqmiao2010-09-08 16:23:20评论(16)24325人阅读 1.问题的引入 在android源码,成功执行了make 和 make sd ...

  9. Android Audio音量设置原理流程分析

    Android Audio音量设置原理流程分析 简介 本篇文章主要介绍Android音量设置从App应用层到framework层执行流程,以及相关的细节和原理分析,建议在阅读此文章前去看博主的混音理论 ...

最新文章

  1. Eclipse文档注释快捷键以及自定义文档注释内容
  2. 基于Open WebRTC Toolkit(OWT)的8K全景视频低延时直播系统
  3. Angular如何对包含了HTTP请求的服务类进行单元测试
  4. 关于jsp中jstl-core标签循环遍历的使用
  5. 【IPC通信】匿名管道
  6. 2020-12-26
  7. Java使用iText PDF导出PDF文档
  8. C语言编程齿轮轮廓线坐标,c语言程序实现齿轮基本参数几何尺寸计算.pdf
  9. 计算机网络(入门知识点最全整理)
  10. 如何在计算机修改wifi密码,wifi修改密码,教您电脑怎么修改wifi密码
  11. Google SketchUp For Dummies
  12. oracle单列转行,oracle 两种列转行的方式
  13. 大班运用计算机教学案例,【大班教学案例】_幼儿园大班教学案例:《让孩子在自主探索中获取成功》...
  14. 雅高集团2021年即将开业的新酒店数量强劲增长
  15. 模仿斗地主玩法实现扑克牌的分发
  16. 机器学习笔记 - 使用Keras Tuner进行自动化超参数调整
  17. 个人的偏好与擅长的领域
  18. 【任务中心】使用说明
  19. 乐优商城之分类查询品牌查询(八)
  20. 电脑鸿蒙系统怎么连接无线网,三星笔记本电脑怎么连接无线网wifi

热门文章

  1. 使用JS-SDK自定义微信分享效果
  2. 03.先了解一些python的基础语法
  3. 2022年中小企业上云首选,华为云省钱攻略
  4. 深度理解PHP执行流程
  5. 移动硬盘和电脑内置硬盘使用时的区别
  6. JavaScript对输入的用户名密码等进行判断
  7. 微信小程序调用update更新数据库数据无效
  8. 服务器被黑客用来挖矿?怎么办?
  9. SAP ABAP BOM 创建 CSAP_MAT_BOM_CREATE 多备选BOM
  10. 独享还是共享,你选择哪一种锁?(独享锁/共享锁)