JAVA并发编程学习笔记之ReentrantLock
ReentrantLock是一个可重入的互斥锁,ReentrantLock由最近成功获取锁,还没有释放的线程所拥有,当锁被另一个线程拥有时,调用lock的线程可以成功获取锁。如果锁已经被当前线程拥有,当前线程会立即返回。此类的构造方法提供一个可选的公平参数
- public ReentrantLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();
- }
公平与非公平有何区别,所谓公平就是严格按照FIFO顺序获取锁,非公平安全由程序员自己设计,比如可以按优先级,也可以按运行次数等规则来选择。
在AQS里面有一个state字段,在ReentrantLock中表示锁被持有的次数,它是一个volatile类型的整型值,因此对它的修改可以保证其他线程可以看到。ReentrantLock顾名思义就是锁可以重入,一个线程持有锁,state=1,如果它再次调用lock方法,那么他将继续拥有这把锁,state=2.当前可重入锁要完全释放,调用了多少次lock方法,还得调用等量的unlock方法来完全释放锁。下面简单看一下它的获取与释放过程:
ReentrantLock实现了Lock接口,获取锁是通过lock方法来实现的,整个过程和AQS的获取过程一样,这里不再分析,只分析它的tryAcquire方法的实现。tryAcquire方法有公平版本与非公平版权,源于ReentrantLock使用了两种同步器,具体使用哪一个是在构造方法中提供的公平参数。先看看公平版本的tryAcquire方法:
- /**
- * Fair version of tryAcquire. Don't grant access unless
- * recursive call or no waiters or is first.
- */
- protected final boolean tryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- if (c == 0) {
- if (!hasQueuedPredecessors() &&
- compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
1、首先判断锁有没有被持有,如果被持有,就判断持有锁的线程是不是当前线程,如果不是就啥也不做,返回获取失败,如果是就增加重入数,返回成功获取;
2、如果锁没有被任何线程持有(c==0),首先判断当前结点前面是否还有线程在排除等待锁,如果有,直接返回获取失败,否则将锁持有数设为acquires,一般为1,然后设置锁的拥有者为当前线程,成功获取。
整个过程比较简单,再来看看非公平的tryAcquire方法:
- /**
- * Performs non-fair tryLock. tryAcquire is
- * implemented in subclasses, but both need nonfair
- * try for trylock method.
- */
- final boolean nonfairTryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- if (c == 0) {
- if (compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0) // overflow
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
代码几乎一模一样,唯一不同的是在知道锁持有数为0时,直接将当前线程设置为锁的持有者,这一点和公平版本的tryAcquire是有区别的,也就是说非公平机制采用的是抢占式模型。
看完了锁的获取,再来看锁的释放,锁的释放就不存在公平与非公平一说。
- protected final boolean tryRelease(int releases) {
- int c = getState() - releases;
- if (Thread.currentThread() != getExclusiveOwnerThread())
- throw new IllegalMonitorStateException();
- boolean free = false;
- if (c == 0) {
- free = true;
- setExclusiveOwnerThread(null);
- }
- setState(c);
- return free;
- }
1、首先判断当前线程是不是拥有锁的线程,如果不是,抛出IllegalMonitorStateException异常,这个异常表明表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程;
2、判断释放之后的锁持有数c,如果c!=0,先设置state为该值,然后返回false表示没有被完全释放。如果c==0,表示锁被完全释放,当前线程释放对锁的拥有,返回true,表示已完全释放。
此外ReentrantLock还有一些其他方法,但大部分都是直接代理了AQS中的方法,顺便提一下,可以使用isHeldByCurrentThread() 和 getHoldCount()方法来检查当前线程是否拥有该锁
- final int getHoldCount() {
- return isHeldExclusively() ? getState() : 0;
- }
- public boolean isHeldByCurrentThread() {
- return sync.isHeldExclusively();
- }
- protected final boolean isHeldExclusively() {
- return getExclusiveOwnerThread() == Thread.currentThread();
- }
JDK文档还提到,可以将isHeldExclusively用于调试和测试,例如,只在保持某个锁时才应调用的方法可以声明如下:
- class X {
- ReentrantLock lock = new ReentrantLock();
- // ...
- public void m() {
- assert lock.isHeldByCurrentThread();
- // ... method body
- }
- }
还可以用此方法来确保某个重入锁是否以非重入方式使用的,例如:
- class X {
- ReentrantLock lock = new ReentrantLock();
- // ...
- public void m() {
- assert !lock.isHeldByCurrentThread();
- lock.lock();
- try {
- // ... method body
- } finally {
- lock.unlock();
- }
- }
- }
参考资料:
Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制
JAVA并发编程学习笔记之ReentrantLock的更多相关文章
- Java并发编程学习笔记
Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...
- Java并发编程学习笔记 深入理解volatile关键字的作用
引言:以前只是看过介绍volatile的文章,对其的理解也只是停留在理论的层面上,由于最近在项目当中用到了关于并发方面的技术,所以下定决心深入研究一下java并发方面的知识.网上关于volatile的 ...
- Java 并发编程学习笔记 理解CLH队列锁算法
CLH算法实现 CLH队列中的结点QNode中含有一个locked字段,该字段若为true表示该线程需要获取锁,且不释放锁,为false表示线程释放了锁.结点之间是通过隐形的链表相连,之所以叫隐形的链 ...
- Java并发编程学习笔记(一)——线程安全性
主要概念:线程安全性.原子性.原子变量.原子操作.竟态条件.复合操作.加锁机制.重入.活跃性与性能. 1.当多个线程访问某个状态变量并且其中有一个线程执行写入操作时,必须采用同步机制来协同这些线程对变 ...
- [转]JAVA并发编程学习笔记之Unsafe类
1.通过Unsafe类可以分配内存,可以释放内存:类中提供的3个本地方法allocateMemory.reallocateMemory.freeMemory分别用于分配内存,扩充内存和释放内存,与C语 ...
- JAVA并发编程学习笔记------多线程调优
1. 多线程场景下尽量使用并发容器代替同步容器 (如ConcurrentHashMap代替同步且基于散列的Map, 遍历操作为主要操作的情况下用CopyOnWriteArrayList代替同步的Lis ...
- Java并发编程学习笔记(三)——对象的组合
重要概念: 1.在设计线程安全类的过程中,需要包含以下三个基本要素: (1)找出构成对象状态的所有变量. (2)找出约束状态变量的不变性条件. (3)建立对象状态的并发访问管理策略. 2.
- java并发编程学习笔记(一)初识并发原子性
1.并发的意义 现在是一个多核的时代,并发的存在意义就是为了能够充分利用多核计算机的优势,提高程序的运行效率: 2.并发的风险 竞争-----多个线程对内存数据数据进行读写操作时,对数据处理结果的一个 ...
- JAVA并发编程学习笔记------协作对象之间发生的死锁
一. 如果在持有锁时调用某个外部方法,那么将出现活跃性问题.在这个外部方法中可能会获取其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁.如下代码: public c ...
随机推荐
- 1080. Graduate Admission (30)
时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue It is said that in 2013, there w ...
- Only the original thread that created a view hierarchy can touch its views
在调试软件的时候出现如下的错误: 01-05 20:53:36.492: E/ZZShip(2043): android.view.ViewRootImpl$CalledFromWrongThread ...
- go语言实现线程池
话说真的好久没有写博客了,最近赶新项目,工作太忙了.这一周任务比较少,又可以随便敲敲了. 逛论坛的时候突发奇想,想用go语言实现一个线程池,主要功能是:添加total个任务到线程池中,线程池开启num ...
- 编辑器&IDE中适合程序员的字体
adobe的免费字体 source Code Pro
- Js 获取当前时间
Js获取当前日期时间及其它操作 var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); //获取完整 ...
- 阅读Google的C++代码规范有感
李开复曾在微博上说过,Google的C++代码规范是全球最好的一份C++代码规范,没有之一.最近花了点时间看了下这份代码规范,收获确实很大,在编程过程中一些乱七八糟的坏习惯也该改一改了.最新的英文版见 ...
- oracle 导出数据和导入数据
导出数据 exp zl_gj/zlkj@gqxt grants=y tables=(zl_gj.ckgj,zl_gj.gjlx,zl_gj.rkgj) file=c:\gj.dmp log=c:\g ...
- C#学习笔记(三)
1.我们在Main()函数中,调用Test()函数,我们管Main()函数称之为调用者,管Test()函数称之为被调用者.如果被调用者想要得到调用者的值:1).传递参数.2).使用静态字段来模拟全局变 ...
- asp防注入安全问题
一.古老的绕验证漏洞虽然古老,依然存在于很多小程序之中,比如一些企业网站的后台,简单谈谈.这个漏洞出现在没有对接受的变量进行过滤,带入数据库判断查询时,造成SQL语句的逻辑问题.例如以下代码存在问题: ...
- 转Spring+Hibernate+EHcache配置(二)
Spring AOP+EHCache简单缓存系统解决方案 需要使用Spring来实现一个Cache简单的解决方案,具体需求如下:使用任意一个现有开源Cache Framework,要求可以Cache系 ...