初识Lock与AbstractQueuedSynchronizer(AQS)
本文转载于:https://juejin.im/post/5aeb055b6fb9a07abf725c8c
一、concurrent包的结构层次
在针对并发编程中,Doug Lea大师为我们提供了大量实用,高性能的工具类,针对这些代码进行研究会让我们队并发编程的掌握更加透彻也会大大提升我们队并发编程技术的热爱。这些代码在java.util.concurrent包下。如下图,即为concurrent包的目录结构图。
其中包含了两个子包:atomic以及lock,另外在concurrent下的阻塞队列以及executors,这些就是concurrent包中的精华,之后会一一进行学习。而这些类的实现主要是依赖于volatile以及CAS,从整体上来看concurrent包的整体实现图如下图所示:
二、lock简介
我们下来看concurent包下的lock子包。锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源。在Lock接口出现之前,java程序主要是靠synchronized关键字实现锁功能的,而java SE5之后,并发包中增加了lock接口,它提供了与synchronized一样的锁功能。虽然它失去了像synchronize关键字隐式加锁解锁的便捷性,但是却拥有了锁获取和释放的可操作性,可中断的获取锁以及超时获取锁等多种synchronized关键字所不具备的同步特性。
通常使用显式使用lock的形式如下:
Lock lock = new ReentrantLock();
lock.lock();
try{.......
}finally{lock.unlock();
}
需要注意的是synchronized同步块执行完成或者遇到异常是锁会自动释放,而lock必须调用unlock()方法释放锁,因此在finally块中释放锁。
- Lock接口API
Lock接口定义的方法:
public interface Lock {void lock(); //获取锁
void lockInterruptibly() throws InterruptedException;//获取锁的过程能够响应中断
boolean tryLock();//非阻塞式响应中断能立即返回,获取锁放回true反之返回fasle
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//超时获取锁,在超时内或者未中断的情况下能够获取锁
Condition newCondition();//获取与lock绑定的等待通知组件,//当前线程必须获得了锁才能进行等待,//进行等待时会先释放锁,当再次获取锁时才能从等待中返回
}
- locks包下有哪些类实现了Lock接口
从最熟悉的ReentrantLock说起
public class ReentrantLock implements Lock, java.io.Serializable
很显然ReentrantLock实现了lock接口,接下来我们来仔细研究一下它是怎样实现的。当你查看源码时你会惊讶的发现ReentrantLock并没有多少代码,另外有一个很明显的特点是:基本上所有的方法的实现实际上都是调用了其静态内存类Sync中的方法,而Sync类继承了AbstractQueuedSynchronizer(AQS)。可以看出要想理解ReentrantLock关键核心在于对队列同步器AbstractQueuedSynchronizer(简称同步器)的理解。
三、初识AQS
- 根据源码注释翻译如下:
同步器是用来构建锁和其他同步组件的基础框架,它的实现主要依赖一个int成员变量来表示同步状态以及通过一个FIFO队列构成等待队列。它的子类必须重写AQS的几个protected修饰的用来改变同步状态的方法,其他方法主要是实现了排队和阻塞机制。状态的更新使用getState,setState以及compareAndSetState这三个方法。
子类被推荐定义为自定义同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅是定义了若干同步状态的获取和释放方法来供自定义同步组件的使用,同步器既支持独占式获取同步状态,也可以支持共享式获取同步状态,这样就可以方便的实现不同类型的同步组件。
同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。可以这样理解二者的关系:锁是面向使用者,它定义了使用者与锁交互的接口,隐藏了实现细节;同步器是面向锁的实现者,它简化了锁的实现方式,屏蔽了同步状态的管理,线程的排队,等待和唤醒等底层操作。锁和同步器很好的隔离了使用者和实现者所需关注的领域。
- AQS的模板方法设计模式
AQS的设计是使用模板方法设计模式,它将一些方法开放给子类进行重写,而同步器给同步组件所提供模板方法又会重新调用被子类所重写的方法。举个例子,AQS中需要重写的方法tryAcquire:
protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}
ReentrantLock中NonfairSync(继承AQS)会重写该方法为:
protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);
}
而AQS中的模板方法acquire():
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
会调用tryAcquire方法,而此时当继承AQS的NonfairSync调用模板方法acquire时就会调用已经被NonfairSync重写的tryAcquire方法。这就是使用AQS的方式,在弄懂这点后会lock的实现理解有很大的提升。可以归纳总结为这么几点:
1.同步组件(这里不仅仅值锁,还包括CountDownLatch等)的实现依赖于同步器AQS,在同步组件实现中,使用AQS的方式被推荐定义继承AQS的静态内存类;
2.AQS采用模板方法进行设计,AQS的protected修饰的方法需要由继承AQS的子类进行重写实现,当调用AQS的子类的方法时就会调用被重写的方法;
3.AQS负责同步状态的管理,线程的排队,等待和唤醒这些底层操作,而Lock等同步组件主要专注于实现同步语义;
4.在重写AQS的方式时,使用AQS提供的getState(),setState(),compareAndSetState()方法进行修改同步状态
AQS可重写的方法如下图(摘自《java并发编程的艺术》一书):
在实现同步组件时AQS提供的模板方法如下图:
- AQS提供的模板方法可以分为3类:
独占式获取与释放同步状态;
共享式获取与释放同步状态;
查询同步队列中等待线程情况;
同步组件通过AQS提供的模板方法实现自己的同步语义。
初识Lock与AbstractQueuedSynchronizer(AQS)相关推荐
- 同步类的基础AbstractQueuedSynchronizer(AQS)
同步类的基础AbstractQueuedSynchronizer(AQS) 我们之前介绍了很多同步类,比如ReentrantLock,Semaphore, CountDownLatch, Reentr ...
- AbstractQueuedSynchronizer(AQS)源码实现
AbstractQueuedSynchronizer(AQS)源码实现 大多数开发者不会直接用到AQS,但是它涵盖的范围极为广泛.AbstractQueuedSynchronizer是并发类诸如Ree ...
- 深入理解AQS(AbstractQueuedSynchronizer)与初识Lock
AQS(java.util.concurrent.locks.AbstractQueuedSynchronizer)是Doug Lea大师创作的用来构建锁或者其他同步组件(信号量.事件等)的基础框架类 ...
- 深入理解AbstractQueuedSynchronizer(AQS)
一.AQS简介 在同步组件的实现中,AQS是核心部分,同步组件的实现者通过使用AQS提供的模板方法实现同步组件语义,AQS则实现了对同步状态的管理,以及对阻塞线程进行排队,等待通知等等一些底层的实现处 ...
- AbstractQueuedSynchronizer AQS源码分析
申明:jdk版本为1.8 AbstractQueuedSynchronizer是jdk中实现锁的一个抽象类,有排他和共享两种模式. 我们这里先看排他模式,共享模式后面结合java.util.concu ...
- java并发编程——九 AbstractQueuedSynchronizer AQS详解
文章目录 AbstractQueuedSynchronizer概述 AbstractQueuedSynchronizer的使用 AQS实现分析 同步队列 独占锁的获取与释放 独占式超时获取 共享式锁的 ...
- Java并发知识梳理(上):并发优缺点,线程状态转换,Java内存模型,Synchronized,Volatile,final,并发三特性,Lock与AQS,ReetrandLock
努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! 整个系列文章为Java并发专题,一是自己的兴趣,二是,这部分在实际理解上很有难度,另外在面试过程中也是经常被问到.所以在学习过程中,记 ...
- 如何在秋招中拿到offer?
努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! 时间总是不知不觉的就溜走了,一晃,学生时代就真的要结束了.前天才吃完了师门的最后的聚餐,痛痛快快的喝了一顿酒.在酒中,对身边的人感恩, ...
- 抽象同步器AQS、CAS应用之--ReentrantLock,lock和unlock的流程、源码分析
文章目录 1. AQS和CAS 1.1 CAS存在的bug:ABA问题 2. ReentrantLock和synchronized的区别 3. ReentrantLock的内部结构 3.1 lock. ...
最新文章
- 清华90后女学霸范楚楚将加入MIT任助理教授,面试宝典分享!
- linux搭建nfs
- Eureka核心知识点
- linux 下搭建postfix服务器
- 美团面试一道场景设计题
- C语言学习笔记---浮点函数modf()和fmod()
- C语言scanf中%%,C语言scanf()和gets()及printf()和puts()的区别
- Android的JNI【实战教程】6⃣️--温控计
- 从SVN检出项目下载到本地后出现错误
- ab并发测试post请求传参
- get方法和post方法的区别和联系
- soui 设置边框_第四篇:SOUI资源文件组织
- 如何用C语言实现批量修改文件类型
- 大数据人工智能应用场景
- matlab符号函数绘图法_[单选] MATLAB中函数()实现符号函数三维曲线的绘图。
- 正定二次型与正定矩阵
- 微信公php开发视频,PHP实现微信公众平台开发 全套视频资源下载
- 空格折叠(幽灵折叠)
- 计算机cfd教学,规整填料CFD模拟X - 天津大学研究生数字化教学(E-LEARNING)平台.pptx...
- JAVA POI WORD XWPFDocument 常用操作-随时更新
热门文章
- kong plugin rate limiting
- 数据分析方法论2——流量分析
- go语言学习---使用os.Args获取简单参数(命令行解析)
- [Xcode 实际操作]八、网络与多线程-(19)使用RunLoop使PerformSelector方法延迟动作的执行...
- 解决Tomcat文件上传超时问题.
- ADO.NET教程(一)
- Lecture 2 Introduction
- windows组件中没有IIS解决方法
- 如何把 DropDownList 某一个 Item 的 Text 改成粗体 ?
- 推荐《求医不如求己》,实用