java中的读写锁

来源:互联网 发布:学美容知乎 编辑:程序博客网 时间:2024/04/28 10:29
在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方法的测试类

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

public class ReadTask extends Thread {
  //logic bean
  private ReadWriteLockLogicreadWriteLockOperator;

  //读者
  private String reader;
  public ReadTask(ReadWriteLockLogicreadWriteLockOperator, 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
     }
    }
  }
}

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

public class WriteTask  extends Thread{
  //logic bean
  private ReadWriteLockLogicreadWriteLockOperator;

  //作者
  private String writer;

  public WriteTask(ReadWriteLockLogicreadWriteLockOperator, 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
    }
  }
}

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

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 = newReentrantReadWriteLock();

  //共享资源
  privateList<String> shareResources = newArrayList<String>(0);

  //读
  public String read() {
    // 得到readLock 并锁定
    LockreadLock = 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 并锁定
    LockwriteLock = 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();//一定要保证锁的释放
    }
  }
}
------------------------------------------------------------------------------------

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) throwsInterruptedException, ExecutionException {
    //1创建一个具有排程功能的线程池
   ScheduledExecutorService service =Executors.newScheduledThreadPool(5);
    //2读写锁的logic bean
   ReadWriteLockLogic lockOperator = new ReadWriteLockLogic();
    //3生成一个可执行任务(该任务执行完毕可以返回结果 或者 抛出异常;而Runnable接口的run方法则不行)
    RunnablewriteTask1 = new WriteTask(lockOperator, "作者A");
    //4延时0秒后每2秒重复执行writeTask1;
   service.scheduleAtFixedRate(writeTask1, 0, 60,TimeUnit.SECONDS);
    //5创建3个读任务
    RunnablereadTask1 = new WriteTask(lockOperator, "作者B");
    RunnablereadTask2 = new ReadTask(lockOperator, "读者B");
    RunnablereadTask3 = new ReadTask(lockOperator, "读者C");
    //6延时0秒后每秒执行一次task1;
   service.scheduleAtFixedRate(readTask1, 1, 1,TimeUnit.SECONDS);
   service.scheduleAtFixedRate(readTask2, 2, 1,TimeUnit.SECONDS);
   service.scheduleAtFixedRate(readTask3, 3, 1,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异常.