java 读写锁详解

在java多线程中,为了提高效率有些共享资源允许同时进行多个读的操作,但只允许一个写的操作,比如一个文件,只要其内容不变可以让多个线程同时读,不必做排他的锁定,排他的锁定只有在写的时候需要,以保证别的线程不会看到数据不完整的文件。

   下面是个关于多线程读写锁的例子,我稍微做了下修改,蛮容易理解的,来至于http://www.highya.com/redirect.php?fid=113&tid=7180&goto=nextoldset。

这里模拟了这样一个场景: 在ReadWriteLockOperator对象里设置一个共享资源 shareResources 。

有3个读者(A, B, C)一直连续的从 shareResources 获取信息, 然后输出到控制台 ;有一个作者每隔60秒往shareResources 加入信息, 加信息的过程相对耗时, 在这段时间, 任何读者都不能访问 shareResources。

  写了4个类来验证这种情况,只在windows下做了测试。

  ReadTask.java       读任务

  WriteTask.java      写任务

  ReadWriteLockLogic.java     读写操作的逻辑

  ReadWriteLockTest.java      带有main方法的测试类

---------------------------------------混哥线-----------------------------------------------    


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

public class ReadTask extends Thread {

  //logic bean

  private ReadWriteLockLogic readWriteLockOperator;

  //读者

  private String reader;

  public ReadTask(ReadWriteLockLogic readWriteLockOperator, String reader) {

    this.readWriteLockOperator = readWriteLockOperator;

    this.reader = reader;

  }

  private ReadTask(){}

  // 执行任务

  public void run() {

    if(this.readWriteLockOperator != null){

      try {

        while(!isInterrupted()){

          Thread.sleep(200);

          System.out.println(reader + " read:" 

          + Thread.currentThread().toString() + " : " this.readWriteLockOperator.read());

        }

      catch (Exception e) {

        // TODO: handle exception

      }

    }

  }

}

-------------------------------------------------------------------------------------


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

public class WriteTask  extends Thread{

  //logic bean

  private ReadWriteLockLogic readWriteLockOperator;

  //作者

  private String writer;

  public WriteTask(ReadWriteLockLogic readWriteLockOperator, String writer) {

    this.readWriteLockOperator = readWriteLockOperator;

    this.writer = writer;

  }

  private WriteTask(){}

  // 一个很耗时的写任务

  public void run() {

    try {

      while(!isInterrupted()){

        Thread.sleep(100);

        this.readWriteLockOperator.write(this.writer, "hehehhe");

      }

    catch (Exception e) {

      // TODO: handle exception

    }

  }

}

----------------------------------------------------------------------------------


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

//读写操作的逻辑

public class ReadWriteLockLogic {

  // 初始化一个 ReadWriteLock

  private ReadWriteLock lock = new ReentrantReadWriteLock();

  //共享资源

  private List<String> shareResources = new ArrayList<String>(0);

  //读

  public String read() {

    // 得到 readLock 并锁定

    Lock readLock = lock.readLock();

    readLock.lock();

    try {

      // 读相对省时,做空循环 大约0.5second

      for(int i=0 ;i<2500000; i++){

        System.out.print("");

      }

      // 做读的工作

      StringBuffer buffer = new StringBuffer();

      for (String shareResource : shareResources) {

        buffer.append(shareResource);

        buffer.append("\t");      

      }

      return buffer.toString();

    finally {

      readLock.unlock();//一定要保证锁的释放

    }

  }

  //写

  public void write(String writer, String content) {

    // 得到 writeLock 并锁定

    Lock writeLock = lock.writeLock();

    writeLock.lock();

    try {

      System.out.println(writer + " write ===" + Thread.currentThread().toString());

      // 写比较费时,所以做空循环 大约13second

      for(int i=0 ;i<10000000; i++){

        System.out.print("");

        System.out.print("");

      }

     

      // 做写的工作

      int count = shareResources.size();

      for (int i=count; i < count + 1; i++) {

        shareResources.add(content + "_" + i);

      }

    finally {

      writeLock.unlock();//一定要保证锁的释放

    }

  }

}

------------------------------------------------------------------------------------


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

import java.util.concurrent.ExecutionException;

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

public class ReadWriteLockTest {

  public static void main(String[] args) throws InterruptedException, ExecutionException {

    //1 创建一个具有排程功能的线程池

    ScheduledExecutorService service = Executors.newScheduledThreadPool(5);

    //2 读写锁的logic bean

    ReadWriteLockLogic lockOperator = new ReadWriteLockLogic();

    //3 生成一个可执行任务(该任务执行完毕可以返回结果 或者 抛出异常;而Runnable接口的run方法则不行)

    Runnable writeTask1 = new WriteTask(lockOperator, "作者A");

    //4 延时0秒后每2秒重复执行writeTask1;

    service.scheduleAtFixedRate(writeTask1, 060, TimeUnit.SECONDS);

    //5 创建3个读任务

    Runnable readTask1 = new WriteTask(lockOperator, "作者B");

    Runnable readTask2 = new ReadTask(lockOperator, "读者B");

    Runnable readTask3 = new ReadTask(lockOperator, "读者C");

    //6 延时0秒后每秒执行一次task1;

    service.scheduleAtFixedRate(readTask1, 11, TimeUnit.SECONDS);

    service.scheduleAtFixedRate(readTask2, 21, TimeUnit.SECONDS);

    service.scheduleAtFixedRate(readTask3, 31, TimeUnit.SECONDS);

   

  }

}

----------------------------------------------------------------------------------------

作者A write ===Thread[pool-1-thread-1,5,main]

作者B write ===Thread[pool-1-thread-4,5,main]

读者C read:Thread[pool-1-thread-3,5,main] : hehehhe_0 hehehhe_1 

读者B read:Thread[pool-1-thread-2,5,main] : hehehhe_0 hehehhe_1

作者A write ===Thread[pool-1-thread-1,5,main]

................

通过观察控制台,可以看到作者a出现后,大约5秒作者b才会出现,而又过了5秒后,读者c和读者b同时会出现,接着5秒后,作者a又出现了。这说明了,读锁之间没有排斥,可以多线程持有并且排斥WriteLock的持有线程。而WriteLock是全部排斥的,是独占的,比较独!

 

下面是附赠的读写锁的小知识,来至http://www.txdnet.cn/essay/view.jsp?tid=1288670091703&cid=2

(a).重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想.

(b).WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持Readlock的持有.反过来ReadLock想要升级为WriteLock则不可能,为什么?参看(a),呵呵.

(c).ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥.这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量.

(d).不管是ReadLock还是WriteLock都支持Interrupt,语义与ReentrantLock一致.

(e).WriteLock支持Condition并且与ReentrantLock语义一致,而ReadLock则不能使用Condition,否则抛出UnsupportedOperationException异常.

特别说明:尊重作者的劳动成果,转载请注明出处哦~~~

时间: 2024-08-08 01:36:51

java 读写锁详解的相关文章

GO语言并发编程之互斥锁、读写锁详解_Golang

在本节,我们对Go语言所提供的与锁有关的API进行说明.这包括了互斥锁和读写锁.我们在第6章描述过互斥锁,但却没有提到过读写锁.这两种锁对于传统的并发程序来说都是非常常用和重要的. 一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开方法--Lock和Unlock.顾名思义,前者被用于锁定当前的互斥量,而后者则被用来对当前的互斥量进行解锁. 类型sy

Java并发控制机制详解_java

在一般性开发中,笔者经常看到很多同学在对待java并发开发模型中只会使用一些基础的方法.比如Volatile,synchronized.像Lock和atomic这类高级并发包很多人并不经常使用.我想大部分原因都是来之于对原理的不属性导致的.在繁忙的开发工作中,又有谁会很准确的把握和使用正确的并发模型呢? 所以最近基于这个思想,本人打算把并发控制机制这部分整理成一篇文章.既是对自己掌握知识的一个回忆,也是希望这篇讲到的类容能帮助到大部分开发者.  并行程序开发不可避免地要涉及多线程.多任务的协作和

Java 线程同步详解_java

Java 线程同步根本上是要符合一个逻辑:加锁------>修改------>释放锁 1.同步代码块 示例如下: public class SyncBlock { static class DataWrap { int i; } static class SyncBlockThread extends Thread { private DataWrap date; public SyncBlockThread(DataWrap dataWrap) { this.date = dataWrap;

Java中文问题详解,底层编码解剖

编码|问题|详解|中文 Java中文问题详解预备知识: 1.字节和unicode Java内核是unicode的,就连class文件也是,但是很多媒体,包括文件/流的保存方式 是使用字节流的. 因此Java要对这些字节流经行转化.char是unicode的,而byte是字节. Java中byte/char互转的函数在sun.io的包中间有.其中ByteToCharConverter类是中调度, 可以用来告诉你,你用的Convertor.其中两个很常用的静态函数是 public static By

Java控制台输入输出详解

初学java时,或许大家都遇到过一个问题,从控制台获取字符,大家最常见的便是通过System.in.read();取得输入的字符,代码如下: public static void receiveOneChar(){//得到一个输入的字符 char ch='2'; System.out.println("please enter a number:"); try { ch=(char)System.in.read(); } catch (IOException e) { e.printS

Java transient 关键字详解及实例代码_java

Java transient 关键字 1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化. 然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见

Java 反射机制详解及实例代码_java

Java反射详解 本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案例1]通过一个对象获得完整的包名和类名 package Reflect; /** * 通过一个对象获得完整的包名和类名 * */ class Demo{ //other codes... } class hello{ public static void main(String[] args) {

java RMI原理详解

[本文转载自java RMI原理详解] 定义 RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法. 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中. Java RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序

java split()函数详解

java split()函数详解 public string[] split(string regex,int limit)根据匹配给定的正则表达式来拆分此字符串. 此方法返回的数组包含此字符串的每个子字符串,这些子字符串由另一个匹配给定的表达式的子字符串终止或由字符串结束来终止.数组中的子字符串按它们在此字符串中的顺序排列.如果表达式不匹配输入的任何部分,则结果数组只具有一个元素,即此字符串 */  string[] timefirs=new string[3];  string timefi