“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

578 阅读12分钟

难度

初级

学习时间

30分钟

适合人群

零基础

开发语言

Java

开发环境

  • JDK v11
  • IntelliJIDEA v2018.3

友情提示

  • 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
  • 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!

1.温故知新

前面在《“全栈2019”Java多线程第四十章:ReadWriteLock读写锁》一章中介绍了读写锁ReadWriteLock

《“全栈2019”Java多线程第四十一章:读锁与写锁之间相互嵌套例子》一章中介绍了读锁与写锁之间相互嵌套情况

现在介绍可重入读写锁ReentrantReadWriteLock的获取线程与读写锁保持数方法

2.可重入读写锁ReentrantReadWriteLock

之前在介绍读写锁ReadWriteLock接口的时候说过,该接口有一个实现类叫ReentrantReadWriteLock,那时由于篇幅原因还没好好介绍它里面的常用方法,本章就来看看它里面有哪些内容。

ReentrantReadWriteLock可重入锁里面有两个内部类:读锁内部类和写锁内部类。这两个内部类的实例正是ReadWriteLock.readLock()方法返回的读锁和ReadWriteLock.writeLock()方法返回的写锁。

读锁内部类:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

写锁内部类:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

读锁内部类和写锁内部类都实现了Lock接口,只不过这写锁比读锁多定义了两个方法:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

  • boolean isHeldByCurrentThread()
  • int getHoldCount()

下面逐一演示这两个方法。

3.查询当前线程是否持有此写锁isHeldByCurrentThread()方法

我们可以通过isHeldByCurrentThread()方法来查询当前线程是否持有此写锁。

为什么是此写锁

因为isHeldByCurrentThread()方法所属WriteLock对象所有,所以通过ReadWriteLock.writeLock()方法获得的写锁对象,调用isHeldByCurrentThread()方法时定当是判断当前线程是否持有调用isHeldByCurrentThread()方法的写锁。

isHeldByCurrentThread()方法在ReentrantReadWriteLock.WriteLock类中的源码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

将注释翻译成中文:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

中文注释全文:

查询当前线程是否持有此写锁,持有返回true,否则返回false。

去掉注释版:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

isHeldByCurrentThread()方法作用是查询当前线程是否持有此写锁,持有返回true,否则返回false。

访问权限

public:isHeldByCurrentThread()方法是公开的。

boolean:isHeldByCurrentThread()方法返回boolean类型的值,当当前线程持有此锁时返回true,否则返回false。

isHeldByCurrentThread()方法只能被对象调用。

参数

无。

抛出的异常

无。

应用

下面,我们就来试试isHeldByCurrentThread()方法。

首先,创建出读写锁,注意此处的类型是ReentrantReadWriteLock类类型,而不是ReadWriteLock接口类型

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

然后,创建出读锁和写锁,注意读锁和写锁都是ReentrantReadWriteLock类中的内部类类型(ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock),而不是Lock接口类型

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

因为isHeldByCurrentThread()方法只有写锁才有,所以我们暂时只需要用到写锁,读锁后面再用:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

写锁同步写好了,在同步内容里使用isHeldByCurrentThread()方法:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

然后,在同步内容外使用isHeldByCurrentThread()方法,看看在获取写锁前后有何不同:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

例子书写完毕。

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

文字版:

查询当前线程是否持有此写锁 --- false
查询当前线程是否持有此写锁 --- true

从运行结果来看,当当前线程未持有此写锁时,isHeldByCurrentThread()方法返回false,否则返回true,即当前线程持有此写锁。

注意

ReentrantReadWriteLock.WriteLock内部类中的isHeldByCurrentThread()方法和ReentrantReadWriteLock中的isWriteLockedByCurrentThread()方法作用相同。

4.查询当前线程是否持有此写锁isWriteLockedByCurrentThread()方法

isWriteLockedByCurrentThread()方法在ReentrantReadWriteLock类中的源码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

将注释翻译成中文:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

中文注释全文:

获取当前线程是否持有此写锁,持有返回true,否则返回false。

去掉注释版:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

isWriteLockedByCurrentThread()方法作用是查询当前线程是否持有此写锁,持有返回true,否则返回false。

访问权限

public:isWriteLockedByCurrentThread()方法是公开的。

boolean:isWriteLockedByCurrentThread()方法返回boolean类型的值,当当前线程持有此锁时返回true,否则返回false。

isWriteLockedByCurrentThread()方法只能被对象调用。

参数

无。

抛出的异常

无。

应用

下面,我们就来试试isWriteLockedByCurrentThread()方法。

还是上一小节的例子,只不过修改以下两处代码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

修改后的代码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

然后,再调用isWriteLockedByCurrentThread()方法:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

例子改写完毕。

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

文字版:

isHeldByCurrentThread --- false
isWriteLockedByCurrentThread --- false
isHeldByCurrentThread --- true
isWriteLockedByCurrentThread --- true

从运行结果来看,符合预期。isWriteLockedByCurrentThread()方法和isHeldByCurrentThread()返回结果一样。

证明ReentrantReadWriteLock.WriteLock.isHeldByCurrentThread()方法和ReentrantReadWriteLock.isWriteLockedByCurrentThread()方法作用相同,请看它们两个方法源码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

ReentrantReadWriteLock.WriteLock

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

ReentrantReadWriteLock

两处调用的方法一致,所以它们两个方法作用是一样的。

5.获取当前线程与锁的保持数getHoldCount()方法

与锁的保持数是什么?

首先,如果锁不支持重入(不清楚重入的小伙伴请查阅《“全栈2019”Java多线程第二十九章:可重入锁与不可重入锁详解》一章),那么当线程拿到锁时,与该锁的保持数永远为1。反之,如果锁支持重入,那么当线程已经拿到锁的情况下仍然继续获取该锁时,与该锁的保持数+1,获取一次锁,保持数就+1,释放一次锁,保持数-1。

不理解没关系,待会来个例子感受一下,请允许我先把getHoldCount()方法介绍完。

getHoldCount()方法在ReentrantReadWriteLock.WriteLock类中的源码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

将注释翻译成中文:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

中文注释全文:

获取当前线程与该锁的保持数。若保持数为0,则说明当前线程未持有该锁。

去掉注释版:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

getHoldCount()方法作用是获取当前线程与该锁的保持数。若保持数为0,则说明当前线程未持有该锁。

访问权限

public:getHoldCount()方法是公开的。

int:getHoldCount()方法返回int类型的值,返回当前线程与该锁的保持数。

getHoldCount()方法只能被对象调用。

参数

无。

抛出的异常

无。

应用

还是上一小节的例子,只不过将以下两处代码移除

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

移除之后,在同步外与内分别调用写锁的getHoldCount()方法

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

例子改写完毕。

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

文字版:

查询当前线程与该锁的保持数 --- 0
查询当前线程与该锁的保持数 --- 1

从运行结果来看,符合预期。

在我们主线程还没有获取到写锁时,与写锁的保持数为0;

在我们主线程第一次获取到写锁时,与写锁的保持数为1。

照此看来,如果我们再获取一次写锁,保持数就再+1?

我们来试试。

修改代码,再获取一次写锁:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

文字版:

查询当前线程与该锁的保持数 --- 0
查询当前线程与该锁的保持数 --- 2

从运行结果来看,符合预期。

获取了两次写锁,与写锁的保持数为2。

释放锁会让保持数-1,我们来看看。

在释放锁的后面输出当前线程与该锁的保持数:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

文字版:

查询当前线程与该锁的保持数 --- 0
查询当前线程与该锁的保持数 --- 2
查询当前线程与该锁的保持数 --- 1

从运行结果来看,符合预期。我们获取了两次写锁,但是只释放了一次写锁,结果正好是1。

注意

ReentrantReadWriteLock.WriteLock内部类中的getHoldCount()方法和ReentrantReadWriteLock中的getWriteHoldCount()方法作用相同。

6.获取当前线程与锁的保持数getWriteHoldCount()方法

getWriteHoldCount()方法在ReentrantReadWriteLock类中的源码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

将注释翻译成中文:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

中文注释全文:

获取当前线程与该锁的保持数。若保持数为0,则说明当前线程未持有该锁。

去掉注释版:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

getWriteHoldCount()方法作用是获取当前线程与该锁的保持数。若保持数为0,则说明当前线程未持有该锁。

访问权限

public:getWriteHoldCount()方法是公开的。

int:getWriteHoldCount()方法返回int类型的值,返回当前线程与该锁的保持数。

getWriteHoldCount()方法只能被对象调用。

参数

无。

抛出的异常

无。

应用

还是上一小节的例子,只不过在每个方法的下面新增一行调用代码,再把前面输出格式换成方法名:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

例子改写完毕。

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

从运行结果来看,符合预期。

证明ReentrantReadWriteLock.WriteLock.getHoldCount()方法和ReentrantReadWriteLock.getWriteHoldCount()方法作用相同,请看它们两个方法源码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

ReentrantReadWriteLock.WriteLock

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

ReentrantReadWriteLock

两处调用的方法一致,所以它们两个方法作用是一样的。

ReentrantReadWriteLock.WriteLock内部类两个自定义方法介绍完了,接下来看看ReentrantReadWriteLock类中有多少其他方法。

7.ReentrantReadWriteLock方法

ReentrantReadWriteLock类中的方法有以下几个:

  • intgetQueueLength()
  • intgetReadHoldCount()
  • intgetReadLockCount()
  • intgetWaitQueueLength​(Condition condition)
  • intgetWriteHoldCount()
  • booleanhasQueuedThread​(Thread thread)
  • booleanhasQueuedThreads()
  • booleanhasWaiters​(Condition condition)
  • booleanisFair()
  • booleanisWriteLocked()
  • booleanisWriteLockedByCurrentThread()

其中getWriteHoldCount()方法isWriteLockedByCurrentThread()方法在上面已经演示过,这里就不再赘述。

由于篇幅限制,本章只介绍其中的getReadHoldCount()方法getReadLockCount()方法。其余方法后面再介绍。

8.获取当前线程与读锁的保持数getReadHoldCount()方法

和getWriteHoldCount()方法类似,getReadHoldCount()方法返回当前线程与读锁的保持数。

还是上一小节的例子,只不过将main()方法里面的写锁改为读锁,然后将输出语句也改掉:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

从运行结果来看,符合预期。我们获取了两次读锁,只释放了一次读锁,正好是上述结果。

9.获取此锁的所有保持数getReadLockCount()方法

getReadLockCount()方法与getReadHoldCount()方法很像,但是它们还是有区别的:

getReadHoldCount()方法是获取当前线程与读锁的保持数;

getReadLockCount()方法是获取读锁的所有保持数。

通过例子来看一下。

还是上一小节的例子,只不过需要移除以下三处代码:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

然后,分别调用getReadLockCount()方法与getReadHoldCount()方法:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

从运行结果来看,两个方法调用之后的结果好像是一样的,看不出什么区别。

但是,下面我们将新添加一个线程,让新添加的线程也去获取/释放读锁,只不过在释放读锁之前需要睡5秒钟

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

运行程序,执行结果:

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

文字版:

获取当前线程与读锁的保持数 --- 0
获取读锁的所有保持数 --- 0
获取当前线程与读锁的保持数 --- 2
获取读锁的所有保持数 --- 3
获取当前线程与读锁的保持数 --- 1
获取读锁的所有保持数 --- 2

从运行结果来看,getReadLockCount()方法与getReadHoldCount()方法调用的结果发生了变化:

当没有线程获取读锁时,getReadLockCount()方法与getReadHoldCount()方法返回值都是0;

当有两个线程获取读锁时,getReadLockCount()方法返回的是这两个线程对读锁的保持数;而getReadHoldCount()方法返回的是当前线程与读锁的保持数;

当其中一个线程释放读锁时,getReadLockCount()方法返回读锁所有保持数-1;而getReadHoldCount()方法情况则不同,若当前释放读锁的线程是当前线程(这句话有点绕,比如主线程释放锁读锁,这就话就是“若当前释放读锁的线程是主线程”,但是当前释放读锁的线程确实是主线程,所以此条件就为真。)则当前线程与读锁的保持数-1,否则(也就是非当前线程释放的读锁)无需-1。

简而言之就是:getReadLockCount()方法获取所有线程与读锁的保持数总和;而getReadHoldCount()方法只是获取当前线程与读锁的保持数。

最后,希望大家可以把这个例子照着写一遍,然后再自己默写一遍,方便以后碰到类似的面试题可以轻松应对。

祝大家编码愉快!

GitHub

本章程序GitHub地址:https://github.com/gorhaf/Java2019/tree/master/Thread/ReadWriteLock

总结

  • ReentrantReadWriteLock.WriteLock类中的isHeldByCurrentThread()方法作用是查询当前线程是否持有此写锁,持有返回true,否则返回false。
  • ReentrantReadWriteLock.WriteLock内部类中的isHeldByCurrentThread()方法和ReentrantReadWriteLock类中的isWriteLockedByCurrentThread()方法作用相同。
  • ReentrantReadWriteLock类中的isWriteLockedByCurrentThread()方法作用是查询当前线程是否持有此写锁,持有返回true,否则返回false。
  • ReentrantReadWriteLock.WriteLock类中的getHoldCount()方法作用是获取当前线程与该锁的保持数。若保持数为0,则说明当前线程未持有该锁。
  • ReentrantReadWriteLock.WriteLock内部类中的getHoldCount()方法和ReentrantReadWriteLock中的getWriteHoldCount()方法作用相同。
  • ReentrantReadWriteLock类中的getWriteHoldCount()方法作用是获取当前线程与该锁的保持数。若保持数为0,则说明当前线程未持有该锁。
  • ReentrantReadWriteLock类中的getReadHoldCount()方法作用是返回当前线程与读锁的保持数。
  • ReentrantReadWriteLock类中的getReadLockCount()方法是获取读锁的所有保持数。
  • getReadLockCount()方法获取所有线程与读锁的保持数总和;而getReadHoldCount()方法只是获取当前线程与读锁的保持数。

至此,Java中获取线程与读写锁的保持数相关内容讲解先告一段落,更多内容请持续关注。

答疑

如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。

上一章

“全栈2019”Java多线程第四十一章:读锁与写锁之间相互嵌套例子

下一章

“全栈2019”Java多线程第四十三章:查询是否有线程在等待读写锁

学习小组

加入同步学习小组,共同交流与进步。

  • 方式一:关注头条号Gorhaf,私信“Java学习小组”。
  • 方式二:关注公众号Gorhaf,回复“Java学习小组”。

全栈工程师学习计划

关注我们,加入“全栈工程师学习计划”。

“全栈2019”Java多线程第四十二章:获取线程与读写锁的保持数

版权声明

原创不易,未经允许不得转载!