`

读写锁的使用

阅读更多

转自:http://www.blogjava.net/junglesong/archive/2008/02/22/181400.html

读写锁的使用

本文内容

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

复习-同步化的概念

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

读写锁应用的场合

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

读写锁的意图

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

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

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

 

lpublic 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字段的用意。
注意并记住其中持续调用及使用随机数的方法。

 

lpublic 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类读写锁的写法.
读写锁相对于线程互斥的优势在于高效,它不会对两个读线程进行盲目的互斥处理,当读线程数量多于写线程尤其如此,当全是写线程时两者等效。

分享到:
评论

相关推荐

    读写锁实现代码

    一般的读写锁代码,都是使用了临界体作为基本锁,这样就出现了在一个线程里获取临界体,但是却在另一个线程里释放临界体的问题。临界体是线程相关锁,必须做到获取...这个读写锁使用临界体和事件的组合解决了这个问题。

    WINDOWS读写锁实现

    参考CRITICAL_SECTION的实现机制,利用CPU的LOCK指令实现读写锁的功能,同时用C++类进行了封装

    windows和linux读写锁C++实现

    windows和linux读写锁C++实现

    读写锁_读写锁_

    读写锁代码可实现对链表的加锁功能,原为并行程序设计基础书121代码实现

    易语言读写锁1.2模块源码

    易语言读写锁1.2模块源码例程程序调用API函数实现读写锁功能的操作。点评:读写锁实际是一种特殊的锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则对共享资源进行写操作。这种锁相...

    C#解决SQlite并发异常问题的方法(使用读写锁)

    本文实例讲述了C#解决SQlite并发异常问题的方法。分享给大家供大家参考,...作者利用读写锁(ReaderWriterLock),达到了多线程安全访问的目标。 using System; using System.Collections.Generic; using System.Text;

    易语言读写锁模块

    易语言读写锁模块源码,读写锁模块,进入,退出,读锁,写锁,解锁,InitializeCriticalSection,DeleteCriticalSection,EnterCriticalSection,LeaveCriticalSection

    mysql读写锁演示案例

    主要通过案例演示了mysql的读写锁问题;帮助理解读写锁的概念、特征。主要通过案例演示了mysql的读写锁问题;帮助理解读写锁的概念、特征。主要通过案例演示了mysql的读写锁问题;帮助理解读写锁的概念、特征。主要...

    linux 读写锁应用实例

    /*使用读写锁实现四个线程读写一段程序的实例,共创建了四个新的线程,其中两个线程用来读取数据,另外两个线程用来写入数据。在任意时刻,如果有一个线程在写数据,将阻塞所有其他线程的任何操作。*/

    PHP程序中的文件锁、互斥锁、读写锁使用技巧解析

    主要介绍了PHP程序中的文件锁、互斥锁、读写锁使用技巧解析,其中重点讲解了sync模块和pthreads模块中的使用实例,需要的朋友可以参考下

    windows 读写锁 (基于关键区的读写锁类 及自动锁)

    读写锁的类 及demo 基于网上的类 改写了几处逻辑不合理的地方 :1、新的读锁来了如已有写锁等待 新的读锁进入等待 2、等待的读锁获取到执行权限后 把正等待的所有读锁全部激活

    java 读写锁代码

    java 读写锁实例代码,付简单注释,测试完整。

    读写锁实现例子

    读写锁实现例子

    简单读写锁实例代码

    简单读写锁实例代码

    C++的线程读写锁(windows实现)

    * 读写锁 * 读-读并发,读-写互斥,写-写互斥 * Windows NT下可以使用 * 基于CRITICAL_SECTION实现,简单高效,只支持在进程内使用,不支持进程间使用 * 支持限制最大并发读者数 * 写操作优先(即当前有线程...

    易语言线程安全之原子锁与读写锁

    @youcanyouup。Tags:线程安全原子锁读写锁。

    08、读写锁ReentrantReadWriteLock&StampLock详解-ev

    读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁...

    关于读写锁算法的Java实现及思考

    关于读写锁算法的Java实现及思考关于读写锁算法的Java实现及思考关于读写锁算法的Java实现及思考关于读写锁算法的Java实现及思考关于读写锁算法的Java实现及思考关于读写锁算法的Java实现及思考关于读写锁算法的Java...

    linux写优先的读写锁设计

    在linux下有两种实现数据互斥的基本机制,包括了信号量,自旋锁。这里要说的读写锁是自旋锁的一个变种

Global site tag (gtag.js) - Google Analytics