iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Java实现读写锁的原理是什么
  • 515
分享到

Java实现读写锁的原理是什么

2023-06-29 16:06:15 515人浏览 薄情痞子
摘要

本文小编为大家详细介绍“Java实现读写锁的原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java实现读写锁的原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。读/写锁Java实现首先我们总结一

本文小编为大家详细介绍“Java实现读写的原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java实现读写锁的原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

读/写锁Java实现

首先我们总结一下获取资源读写权限的条件:

读取权限,如果没有线程在写,并且没有线程请求写访问。

写访问,如果没有线程正在读取或写入。

如果一个线程想要读取资源,只要没有线程正在写入,并且没有线程请求对该资源的写访问,就可以了。通过提高写访问请求的优先级,我们假设写请求比读请求更重要。此外,如果读取是最常发生的事情,并且我们没有提高写入的优先级,则可能会发生饥饿。请求写访问的线程将被阻止,直到所有读者都解锁了ReadWriteLock. 如果新线程不断被授予读访问权限,则等待写访问权限的线程将无限期地保持阻塞,从而导致饥饿。因此,如果当前没有线程锁定线程,则只能授予线程读取访问权限ReadWriteLock写作,或要求锁定写作。

可以授予想要对资源进行写访问的线程,因此当没有线程正在读取或写入资源时。有多少线程请求写访问或按什么顺序都没有关系,除非您想保证请求写访问的线程之间的公平性。

考虑到这些简单的规则,我们可以实现ReadWriteLock如下所示:

公共类读写锁{  私人 int 读者 = 0;  私人 int 作家 = 0;  私人 int writeRequests = 0;  公共同步 void lockRead() 抛出 InterruptedException{    而(作家> 0 || writeRequests > 0){      等待();    }    读者++;  }  公共同步无效解锁读取(){    读者——;    通知所有();  }  公共同步 void lockWrite() 抛出 InterruptedException{    写请求++;    而(读者> 0 ||作家> 0){      等待();    }    写请求——;    作家++;  }  公共同步 void unlockWrite() 抛出 InterruptedException{    作家——;    通知所有();  }}

有ReadWriteLock两种锁定方法和两种解锁方法。一种用于读取访问的锁定和解锁方法,一种用于写入访问的锁定和解锁方法。

读取访问的规则在该lockRead()方法中实现。所有线程都获得读访问权限,除非有一个线程具有写访问权限,或者一个或多个线程请求了写访问权限。

写访问的规则在lockWrite()方法中实现。想要写访问的线程从请求写访问开始(writeRequests++)。然后它将检查它是否真的可以获得写访问权限。如果没有对资源具有读访问权限的线程,并且没有对资源具有写访问权限的线程,则线程可以获得写访问权限。有多少线程请求写访问并不重要。

值得注意的是,两者都是unlockRead()andunlockWrite()调用 notifyAll()而不是notify(). 要解释为什么会这样,请想象以下情况:

在 ReadWriteLock 内部有等待读访问的线程和等待写访问的线程。如果被唤醒的线程notify()是读访问线程,它将被放回等待,因为有线程在等待写访问。但是,没有一个等待写访问的线程被唤醒,所以没有更多的事情发生。没有线程既不能读也不能写。通过调用noftifyAll()唤醒所有等待的线程并检查它们是否可以获得所需的访问权限。

打电话notifyAll()还有另一个好处。如果多个线程正在等待读取访问,而没有一个线程正在等待写入访问,并且unlockWrite()被调用,则所有等待读取访问的线程都被立即授予读取访问权限 - 而不是一个接一个。

读/写锁重入

前面显示的ReadWriteLock类是不可重入的。如果一个具有写访问权限的线程再次请求它,它将阻塞,因为已经有一个写者——它自己。此外,考虑这种情况:

线程 1 获得读取权限。

线程 2 请求写访问,但由于只有一个读取器而被阻止。

线程1重新请求读访问(重新入锁),但是因为有写请求而被阻塞

在这种情况下,前一个ReadWriteLock会锁定 - 类似于死锁的情况。不会授予既不请求读取也不请求写入访问的线程。

要使ReadWriteLock可重入,有必要进行一些更改。读者和作者的重入将分别处理。

读取重入

为了让ReadWriteLock读者可以重入,我们首先要建立阅读重入的规则:

如果线程可以获得读取访问权限(没有写入者或写入请求),或者如果它已经具有读取访问权限(无论写入请求如何),它就会被授予读取重入权限。

为了确定一个线程是否已经具有读访问权限,对每个被授予读访问权限的线程的引用以及它获得读锁的次数都保存在 Map 中。在确定是否可以授予读取访问权限时,将检查此 Map 是否对调用线程的引用。以下是更改后lockRead()andunlockRead()方法的外观:

公共类读写锁{  私有 Map<Thread, Integer> readingThreads =      新的 HashMap<Thread, Integer>();  私人 int 作家 = 0;  私人 int writeRequests = 0;  公共同步 void lockRead() 抛出 InterruptedException{    线程调用Thread = Thread.currentThread();    而(!canGrantReadAccess(调用线程)){      等待();                                                                       }    readingThreads.put(调用线程,       (getAccessCount(callingThread) + 1));  }  公共同步无效解锁读取(){    线程调用Thread = Thread.currentThread();    int accessCount = getAccessCount(callingThread);    if(accessCount == 1){ readingThreads.remove(callingThread); }    否则 { readingThreads.put(callingThread, (accessCount -1)); }    通知所有();  }  私有布尔canGrantReadAccess(线程调用线程){    如果(作家> 0)返回假;    如果(isReader(调用线程)返回真;    如果(writeRequests > 0)返回假;    返回真;  }  私有 int getReadAccessCount(线程调用线程){    整数 accessCount = readingThreads.get(callingThread);    if(accessCount == null) 返回 0;    返回 accessCount.intValue();  }  私有布尔 isReader(线程调用线程){    返回阅读Threads.get(callingThread) != null;  }}

如您所见,仅当当前没有线程写入资源时才授予读取重入。此外,如果调用线程已经具有读取访问权限,则这优先于任何 writeRequests。

写重入

仅当线程已经具有写访问权限时才授予写重入。以下是更改后的lockWrite()andunlockWrite()方法:

公共类读写锁{    私有 Map<Thread, Integer> readingThreads =        新的 HashMap<Thread, Integer>();    私有 int writeAccesses = 0;    私人 int writeRequests = 0;    私有线程写作Thread = null;  公共同步 void lockWrite() 抛出 InterruptedException{    写请求++;    线程调用Thread = Thread.currentThread();    而(!canGrantWriteAccess(调用线程)){      等待();    }    写请求——;    写访问++;    写线程 = 调用线程;  }  公共同步 void unlockWrite() 抛出 InterruptedException{    写访问——;    如果(writeAccesses == 0){      写线程=空;    }    通知所有();  }  私有布尔canGrantWriteAccess(线程调用线程){    如果(hasReaders())返回假;    if(writingThread == null) 返回真;    if(!isWriter(callingThread)) 返回假;    返回真;  }  私有布尔 hasReaders(){    返回读数Threads.size() > 0;  }  私有布尔 isWriter(线程调用线程){    返回写线程 == 调用线程;  }}

请注意,在确定调用线程是否可以获得写访问权时,现在如何考虑当前持有写锁的线程。

读写重入

有时,具有读访问权限的线程也需要获得写访问权限。为此,线程必须是唯一的读者。为了实现这一点,writeLock()应该稍微改变方法。这是它的样子:

公共类读写锁{    私有 Map<Thread, Integer> readingThreads =        新的 HashMap<Thread, Integer>();    私有 int writeAccesses = 0;    私人 int writeRequests = 0;    私有线程写作Thread = null;  公共同步 void lockWrite() 抛出 InterruptedException{    写请求++;    线程调用Thread = Thread.currentThread();    而(!canGrantWriteAccess(调用线程)){      等待();    }    写请求——;    写访问++;    写线程 = 调用线程;  }  公共同步 void unlockWrite() 抛出 InterruptedException{    写访问——;    如果(writeAccesses == 0){      写线程=空;    }    通知所有();  }  私有布尔canGrantWriteAccess(线程调用线程){    if(isOnlyReader(callingThread)) 返回真;    如果(hasReaders())返回假;    if(writingThread == null) 返回真;    if(!isWriter(callingThread)) 返回假;    返回真;  }  私有布尔 hasReaders(){    返回读数Threads.size() > 0;  }  私有布尔 isWriter(线程调用线程){    返回写线程 == 调用线程;  }  私有布尔 isOnlyReader(线程线程){      返回读数Threads.size() == 1 &&             readingThreads.get(callingThread) != null;      } }

现在ReadWriteLock该类是读写访问可重入的。

写读重入

有时,具有写访问权限的线程也需要读访问权限。如果请求,应始终授予写入者读取访问权限。如果一个线程有写访问权限,其他线程就不能有读或写访问权限,所以它并不危险。以下是该 canGrantReadAccess()方法在更改后的外观:

公共类读写锁{    私有布尔canGrantReadAccess(线程调用线程){      if(isWriter(callingThread)) 返回真;      如果(写线程!= null)返回false;      如果(isReader(调用线程)返回真;      如果(writeRequests > 0)返回假;      返回真;    }}

完全可重入读写锁

下面是完全可重入的ReadWriteLock实现。我对访问条件进行了一些重构,以使它们更易于阅读,从而更容易说服自己它们是正确的。

公共类读写锁{  私有 Map<Thread, Integer> readingThreads =       新的 HashMap<Thread, Integer>();   私有 int writeAccesses = 0;   私人 int writeRequests = 0;   私有线程写作Thread = null;  公共同步 void lockRead() 抛出 InterruptedException{    线程调用Thread = Thread.currentThread();    而(!canGrantReadAccess(调用线程)){      等待();    }    readingThreads.put(调用线程,     (getReadAccessCount(callingThread) + 1));  }  私有布尔canGrantReadAccess(线程调用线程){    if( isWriter(callingThread) ) 返回真;    if( hasWriter() ) 返回假;    if( isReader(callingThread) ) 返回真;    if( hasWriteRequests() ) 返回假;    返回真;  }  公共同步无效解锁读取(){    线程调用Thread = Thread.currentThread();    如果(!isReader(调用线程)){      throw new IllegalMonitorStateException("调用线程没有" +        " 持有此 ReadWriteLock 的读锁");    }    int accessCount = getReadAccessCount(callingThread);    if(accessCount == 1){ readingThreads.remove(callingThread); }    否则 { readingThreads.put(callingThread, (accessCount -1)); }    通知所有();  }  公共同步 void lockWrite() 抛出 InterruptedException{    写请求++;    线程调用Thread = Thread.currentThread();    而(!canGrantWriteAccess(调用线程)){      等待();    }    写请求——;    写访问++;    写线程 = 调用线程;  }  公共同步 void unlockWrite() 抛出 InterruptedException{    if(!isWriter(Thread.currentThread()){      throw new IllegalMonitorStateException("调用线程没有" +        " 持有这个 ReadWriteLock 的写锁");    }    写访问——;    如果(writeAccesses == 0){      写线程=空;    }    通知所有();  }  私有布尔canGrantWriteAccess(线程调用线程){    if(isOnlyReader(callingThread)) 返回真;    如果(hasReaders())返回假;    if(writingThread == null) 返回真;    if(!isWriter(callingThread)) 返回假;    返回真;  }  私有 int getReadAccessCount(线程调用线程){    整数 accessCount = readingThreads.get(callingThread);    if(accessCount == null) 返回 0;    返回 accessCount.intValue();  }  私有布尔 hasReaders(){    返回读数Threads.size() > 0;  }  私有布尔 isReader(线程调用线程){    返回阅读Threads.get(callingThread) != null;  }  私有布尔 isOnlyReader(线程调用线程){    返回读数Threads.size() == 1 &&           readingThreads.get(callingThread) != null;  }  私有布尔 hasWriter(){    返回写作线程!= null;  }  私有布尔 isWriter(线程调用线程){    返回写线程 == 调用线程;  }  私有布尔 hasWriteRequests(){      返回 this.writeRequests > 0;  }}

从 finally 子句调用 unlock()

当用 保护临界区时ReadWriteLock,临界区可能会抛出异常,从 - 子句内部调用readUnlock()和writeUnlock()方法很重要finally。这样做可以确保ReadWriteLock已解锁,以便其他线程可以锁定它。这是一个例子:

lock.lockWrite();尝试{  //做临界区代码,可能会抛出异常} 最后 {  lock.unlockWrite();}

这个小结构确保ReadWriteLock在关键部分的代码抛出异常的情况下解锁。如果unlockWrite() 没有从 - 子句内部调用finally,并且从临界区抛出异常,ReadWriteLock则将永远保持写锁定,导致调用该实例的所有线程lockRead()或lockWrite()在该 ReadWriteLock实例上无限期停止。唯一可以解锁的ReadWriteLock方法是如果 ReadWriteLock是可重入的,并且在抛出异常时锁定它的线程后来成功锁定它,执行关键部分并unlockWrite() 随后再次调用。那将ReadWriteLock再次解锁。但为什么要等到这种情况发生,如果它发生了吗?unlockWrite()从 -子句调用finally是一个更健壮的解决方案。

读到这里,这篇“Java实现读写锁的原理是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网精选频道。

--结束END--

本文标题: Java实现读写锁的原理是什么

本文链接: https://www.lsjlt.com/news/325739.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java实现读写锁的原理是什么
    本文小编为大家详细介绍“Java实现读写锁的原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java实现读写锁的原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。读/写锁Java实现首先我们总结一...
    99+
    2023-06-29
  • java读写锁的工作原理是什么
    读写锁是一种特殊的锁机制,允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。读写锁的工作原理如下: 当一个线程想要读取...
    99+
    2024-04-03
    java
  • 详解Java ReentrantReadWriteLock读写锁的原理与实现
    目录概述原理概述加锁原理图解过程源码解析解锁原理图解过程源码解析概述 ReentrantReadWriteLock读写锁是使用AQS的集大成者,用了独占模式和共享模式。本文和大家一起...
    99+
    2022-11-13
    Java ReentrantReadWriteLock读写锁 Java ReentrantReadWriteLock Java 读写锁
  • golang锁的实现原理是什么
    golang锁的实现原理是通过互斥锁和读写锁来保护共享资源的访问。互斥锁是一种基本的锁机制,用于保护共享资源,使用一个标志位来表示资源是否被占用,当一个goroutine获取到互斥锁后,其他goroutine就会被阻塞,直到该gorouti...
    99+
    2023-12-12
    Golang
  • java读写锁的使用方法是什么
    在Java中,读写锁是一种特殊的锁,允许多个线程同时读取共享资源,但只允许一个线程进行写操作。读写锁通过ReadWriteLock接...
    99+
    2024-04-03
    java
  • mysql读写分离的原理是什么
    MySQL读写分离是指将数据库的读操作和写操作分开到不同的数据库服务器上,以达到提高数据库性能和可靠性的目的。其原理如下: 主从...
    99+
    2024-03-15
    mysql
  • Java中读写锁ReadWriteLock的原理与应用详解
    目录什么是读写锁?为什么需要读写锁?读写锁的特点读写锁的使用场景读写锁的主要成员和结构图读写锁的实现原理读写锁总结Java并发编程提供了读写锁,主要用于读多写少的场景,今天我就重点来...
    99+
    2024-04-02
  • mysql行级锁的实现原理是什么
    MySQL行级锁的实现原理是通过两种方式来实现的:锁的粒度和锁的类型。 锁的粒度: MySQL的行级锁是在InnoDB存储引擎中...
    99+
    2024-04-09
    mysql
  • Linux互斥锁的实现原理是什么
    本篇内容主要讲解“Linux互斥锁的实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux互斥锁的实现原理是什么”吧!互斥锁(Mutex)是在原子操作API的基础上实现的信号量行...
    99+
    2023-06-28
  • linux内核锁的实现原理是什么
    Linux内核锁的实现原理是通过硬件的原子操作指令或者特殊的指令序列来保证对共享资源的原子操作,从而实现线程之间的同步和互斥。Lin...
    99+
    2023-10-21
    linux
  • MySQL读写分离原理是什么
    这篇文章主要讲解了“MySQL读写分离原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“MySQL读写分离原理是什么”吧!一、读写分离的概念读写分离是基于主从复制来实现的。在实际的应用...
    99+
    2023-07-02
  • 如何理解golang里面的读写锁实现与核心原理
    如何理解golang里面的读写锁实现与核心原理,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。基础筑基读写锁的特点读写锁区别与互斥锁的主要区别就是读锁之间是共享的...
    99+
    2023-06-19
  • redis分布式锁的实现原理是什么
    这篇文章主要讲解了“redis分布式锁的实现原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“redis分布式锁的实现原理是什么”吧!借助于redis...
    99+
    2024-04-02
  • java读写锁怎么使用及优点是什么
    这篇文章主要介绍了java读写锁怎么使用及优点是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java读写锁怎么使用及优点是什么文章都会有所收获,下面我们一起来看看吧。前言:读写锁(Readers-Writ...
    99+
    2023-06-30
  • 一致性读实现原理是什么
    本篇内容主要讲解“一致性读实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“一致性读实现原理是什么”吧!MySQL中的事务事务在RDBMS系统中概念基...
    99+
    2024-04-02
  • java中自旋锁的原理是什么
    本篇文章给大家分享的是有关java中自旋锁的原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java可以用来干什么Java主要应用于:1. web开发;2. Andro...
    99+
    2023-06-14
  • Java AQS的实现原理是什么
    这篇文章主要介绍“Java AQS的实现原理是什么”,在日常操作中,相信很多人在Java AQS的实现原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java AQS的实...
    99+
    2023-07-05
  • java原子类实现的原理是什么
    Java原子类的实现原理是利用了底层的CAS(Compare and Swap)操作。CAS是一种乐观锁机制,它包含三个参数:内存位...
    99+
    2023-10-18
    java
  • java lock的实现原理是什么
    Java中的锁(Lock)是一种同步机制,用于控制多个线程对共享资源的访问。锁的主要作用是确保在同一时刻只有一个线程能够访问某个共享...
    99+
    2023-10-20
    java
  • Java读写锁ReadWriteLock原理与应用场景详解
    Java并发编程提供了读写锁,主要用于读多写少的场景 什么是读写锁? 读写锁并不是JAVA所特有的读写锁(Readers-Writer Lock)顾名思义是一把锁分为两部分:读锁和写...
    99+
    2023-02-18
    Java读写锁ReadWriteLock原理 Java读写锁应用场景 Java读写锁的特点 读写状态的设计 写锁的获取与释放 锁降级
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作