读/写锁的实现和应用(高并发状态下的map实现)

程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需要一个读/写锁来解决这个问题。

按照上面的叙述,简单的实现出一个读/写锁

public class ReadWriteLock{

  private int readers = 0;

  private int writers = 0;

  private int writeRequests = 0;

  public synchronized void lockRead()

      throws InterruptedException{

      while(writers > 0 || writeRequests > 0){

          wait();

      }

      readers++;

  }

  public synchronized void unlockRead(){

      readers--;

      notifyAll();

  }

  public synchronized void lockWrite()

      throws InterruptedException{

      writeRequests++;

      while(readers > 0 || writers > 0){

          wait();

      }

      writeRequests--;

      writers++;

  }

  public synchronized void unlockWrite()

      throws InterruptedException{

      writers--;

      notifyAll();

  }

}

ReadWriteLock类中,读锁和写锁各有一个获取锁和释放锁的方法。

可重入的ReadWriteLock的完整实现

下面是完整的ReadWriteLock实现。为了便于代码的阅读与理解,简单对上面的代码做了重构。重构后的代码如下。

public class ReadWriteLock{

 private Map<Thread, Integer> readingThreads =

     new HashMap<Thread, Integer>();

 private int writeAccesses    = 0;

 private int writeRequests    = 0;

 private Thread writingThread = null;

 public synchronized void lockRead()

     throws InterruptedException{

     Thread callingThread = Thread.currentThread();

     while(! canGrantReadAccess(callingThread)){

         wait();

     }

     readingThreads.put(callingThread,

         (getReadAccessCount(callingThread) + 1));

 }

 private boolean canGrantReadAccess(Thread callingThread){

     if(isWriter(callingThread)) return true;

     if(hasWriter()) return false;

     if(isReader(callingThread)) return true;

     if(hasWriteRequests()) return false;

     return true;

 }

 public synchronized void unlockRead(){

     Thread callingThread = Thread.currentThread();

     if(!isReader(callingThread)){

         throw new IllegalMonitorStateException(

             "Calling Thread does not" +

             " hold a read lock on this ReadWriteLock");

     }

     int accessCount = getReadAccessCount(callingThread);

     if(accessCount == 1){

         readingThreads.remove(callingThread);

     } else {

         readingThreads.put(callingThread, (accessCount -1));

     }

     notifyAll();

 }

 public synchronized void lockWrite()

     throws InterruptedException{

     writeRequests++;

     Thread callingThread = Thread.currentThread();

     while(!canGrantWriteAccess(callingThread)){

         wait();

     }

     writeRequests--;

     writeAccesses++;

     writingThread = callingThread;

 }

 public synchronized void unlockWrite()

     throws InterruptedException{

     if(!isWriter(Thread.currentThread()){

     throw new IllegalMonitorStateException(

         "Calling Thread does not" +

         " hold the write lock on this ReadWriteLock");

     }

     writeAccesses--;

     if(writeAccesses == 0){

         writingThread = null;

     }

     notifyAll();

 }

 private boolean canGrantWriteAccess(Thread callingThread){

     if(isOnlyReader(callingThread)) return true;

     if(hasReaders()) return false;

     if(writingThread == null) return true;

     if(!isWriter(callingThread)) return false;

     return true;

 }

 private int getReadAccessCount(Thread callingThread){

     Integer accessCount = readingThreads.get(callingThread);

     if(accessCount == null) return 0;

     return accessCount.intValue();

 }

 private boolean hasReaders(){

     return readingThreads.size() > 0;

 }

 private boolean isReader(Thread callingThread){

     return readingThreads.get(callingThread) != null;

 }

 private boolean isOnlyReader(Thread callingThread){

     return readingThreads.size() == 1 &&

         readingThreads.get(callingThread) != null;

 }

 private boolean hasWriter(){

     return writingThread != null;

 }

 private boolean isWriter(Thread callingThread){

     return writingThread == callingThread;

 }

 private boolean hasWriteRequests(){

     return this.writeRequests > 0;

 }

}

应用:线程安全并且高并发状态下的map实现

class RWDictionary {
    private final Map<String, Data> m = new TreeMap<String, Data>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data get(String key) {
        r.lock();
        try { return m.get(key); }
        finally { r.unlock(); }
    }
    public String[] allKeys() {
        r.lock();
        try { return m.keySet().toArray(); }
        finally { r.unlock(); }
    }
    public Data put(String key, Data value) {
        w.lock();
        try { return m.put(key, value); }
        finally { w.unlock(); }
    }
    public void clear() {
        w.lock();
        try { m.clear(); }
        finally { w.unlock(); }
    }
 }
时间: 2024-12-29 17:37:44

读/写锁的实现和应用(高并发状态下的map实现)的相关文章

高并发状态下修改数据库的操作

在高并发状态下,尤其数据在频繁修改的状态下,很可能出现脏数据,也有可能造成脏读,不可重复读等问题,那么怎么解决这种问题呢,其实解决方式有很多中,我们探讨出来的结果是,在频繁修改的表里面添加一个时间戳或者随机数的字段,例如,timestamp. 原理是当每一次修改数据的时候都要把之前的时间戳修改成当前的时间戳,并且之前的时间戳要作为where条件,如果之前的时间戳和参数里面的时间戳不一致,则修改失败,在重新查询进行反复操作,上代码: try {  if (count>5) {    return 

php高并发状态下文件的读写

背景 1.对于PV不高或者说并发数不是很大的应用,不用考虑这些,一般的文件操作方法完全没有问题 2.如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失 例如:一个在线聊天室(这里假定把聊天内容写入文件),在同一时刻,用户A和用户B都要操作数据保存文件,首先是A打开了文件,然后更新里面的数据,但这 里B也正好也打开了同一个文件,也准备更新里面的数据.当A把写好的文件保存时,这里其实B已经打开了文件.但当B再把文件保存

C# 防止同时调用=========使用读写锁三行代码简单解决多线程并发的问题

http://www.jb51.net/article/99718.htm 本文主要介绍了C#使用读写锁三行代码简单解决多线程并发写入文件时提示"文件正在由另一进程使用,因此该进程无法访问此文件"的问题.需要的朋友可以参考借鉴 在开发程序的过程中,难免少不了写入错误日志这个关键功能.实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件. 选择最后一种方法实现的时候,若对文件操作与线程同步不熟悉,问题就有可能出现了,因为同一个文

高并发的下的数据库设计

现在都什么年代了,还拿着sql server mysql oracle 读取数据呢,好你们遇到的问题我来给你们解答下 在高并发情况下,谈谈数据库与Redis之间如何进行数据同步(写,读),即能保证业务的完整性,又能提升性能 方式1:数据库保存数据,redis不persistredis启动后,从数据库加载数据不要求强一致实时性的读请求,都由redis处理要求强一致实时性的读请求,由数据库处理写请求有2种处理方式,由数据库处理- 应用先写道数据库,然后更新redis- 应用先写道数据库,然后其它da

大话程序猿眼里的高并发(下)

大话程序猿眼里的高并发(下) 前言 高并发经常会发生在有大活跃用户量,用户高聚集的业务场景中,如:秒杀活动,定时领取红包等. 为了让业务可以流畅的运行并且给用户一个好的交互体验,我们需要根据业务场景预估达到的并发量等因素,来设计适合自己业务场景的高并发处理方案. 在电商相关产品开发的这些年,我有幸的遇到了并发下的各种坑,这一路摸爬滚打过来有着不少的血泪史,这里进行的总结,作为自己的归档记录,同时分享给大家. 服务器架构 业务从发展的初期到逐渐成熟,服务器架构也是从相对单一到集群,再到分布式服务.

高并发场景下缓存+数据库双写不一致问题分析与解决方案设计

Redis是企业级系统高并发.高可用架构中非常重要的一个环节.Redis主要解决了关系型数据库并发量低的问题,有助于缓解关系型数据库在高并发场景下的压力,提高系统的吞吐量(具体Redis是如何提高系统的性能.吞吐量,后面会专门讲). 而我们在Redis的实际使用过程中,难免会遇到缓存与数据库双写时数据不一致的问题,这也是我们必须要考虑的问题.如果还有同学不了解这个问题,可以搬小板凳来听听啦. 一.数据库+缓存双写不一致问题引入 要讲数据库+缓存双写不一致的问题,就需要先讲一下这个问题是怎么发生的

Jackson高并发情况下,产生阻塞

情况:在高并发情况下,查看线程栈信息,有大量的线程BLOCKED. 从线程栈得知,线程栈中出现了阻塞,锁在了com.fasterxml.jackson.databind.ser.SerializerCache.untypedValueSerializer(SerializerCache.java:74)上. 1 "catalina-exec-1453" #1525 daemon prio=5 os_prio=0 tid=0x00007f1010098800 nid=0x2675 wai

缓存在高并发场景下的常见问题

缓存一致性问题 当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象.这就比较依赖缓存的过期和更新策略.一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存. 缓存并发问题 缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程.但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致 “雪崩”现象.此外,当某个缓存key在被更新时,同时也可能被大量请求在获取,

WCF服务在高并发情况下报目标积极拒绝的异常处理 z

http://www.cnblogs.com/kklldog/p/5037006.html wcf的监控服务,偶尔监控到目标服务会报一个目标积极拒绝的错误.一开始以为服务停止了,上服务器检查目标服务好好的活着.于是开始查原因. 一般来说目标积极拒绝(TCP 10061)的异常主要是2种可能: 1:服务器关机或者服务关闭 2:Client调用的端口错误或者服务器防火墙没开相应的端口 但是我们的服务本身是可以调用的,只是偶尔报这个错误,说明并不是这2个问题造成的.继续google,在stackove