java的aqs是什么,AQS在Java中的应用

上篇文章我们详细分析了AQS的底层实现原理,这节就来探索jdk中使用AQS实现的工具类

ReentrantLock

一, 是什么?怎么用?

是什么?是一个独占锁,也就是在并发环境下同一时刻只能有一个线程获得资源,也是一个可重入锁.

可重入锁: 一个线程已经获取到了该资源,下次再次获取资源时不会出现等待情况(上次获取资源没有释放)

怎么用?在各类并发的场景下,为了保证资源获取的正确性,可以保证每个资源同时只能被一个线程获取到.

例如: 宿舍选宿系统(每张床位只能有一个学生抢到),秒杀活动(同一件商品不能被两个人买走)

二, 类架构

1460000037518908由上面架构图可以看出,ReentrantLock可以分为公平锁和非公平锁,而底层实现是AQS,在后面我们还可以看到更多的类底层都是由AQS实现的,所以说熟悉AQS原理对理解这些类是十分有必要的

类的属性/**

* 实现锁的同步器

*/

private final Sync sync;/**

* 抽象同步器

* 子类可有公平和非公平两种方式,使用AQS的state字段来表示是否获取到锁和重入次数

*/

abstract static class Sync extends AbstractQueuedSynchronizer {

private static final long serialVersionUID = -5179523762034025860L;

/**

* 根据子类实现,可以实现公平锁和非公平锁

*/

abstract void lock();

/**

* 非公平方式获取锁

*/

final boolean nonfairTryAcquire(int acquires) {

final Thread current = Thread.currentThread();

int c = getState();

//如果当前状态值为0也就是当前锁没有被其他线程持有,则尝试获取锁

if (c == 0) {

//获取锁,如果过去成功,则设置当前线程为独占线程

if (compareAndSetState(0, acquires)) {

setExclusiveOwnerThread(current);

return true;

}

}

//如果当前锁是当前线程所持有,则将重入次数+1

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;

}

/**

* 释放锁

*/

@Override

protected final boolean tryRelease(int releases) {

int c = getState() - releases;

//判断当前线程和锁持有线程是否为同一个线程

if (Thread.currentThread() != getExclusiveOwnerThread())

throw new IllegalMonitorStateException();

boolean free = false;

//判断当前可重入次数是否为0,如果为0则清除线程占有标记

if (c == 0) {

free = true;

//清除掉独占标记

setExclusiveOwnerThread(null);

}

setState(c);

return free;

}

/**

* 判断当前线程是否持有锁

*/

@Override

protected final boolean isHeldExclusively() {

return getExclusiveOwnerThread() == Thread.currentThread();

}

/**

* 创建条件变量

*/

final ConditionObject newCondition() {

return new ConditionObject();

}

/**

* 获取资源持有者

*/

final Thread getOwner() {

return getState() == 0 ? null : getExclusiveOwnerThread();

}

/**

* 获取重入次数

*/

final int getHoldCount() {

return isHeldExclusively() ? getState() : 0;

}

/**

* 是否已经持有锁

*/

final boolean isLocked() {

return getState() != 0;

}

}

三, 具体实现

公平式先获取资源的状态,如果没有人占用,判断当前线程是否为队列的首节点,如果是则尝试获取资源,获取成功修改独占线程,如果有人占用则判断独占线程和当前线程是否相同,如果相同的判断可重入的次数,超过抛出错误,否则重入成功/**

* 公平锁

*/

static final class FairSync extends Sync {

private static final long serialVersionUID = -3000897897090466540L;

@Override

final void lock() {

acquire(1);

}

/**

* 获取锁

*/

@Override

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;

}

}

非公平式先获取当前资源的状态,如果没有人占用,直接获取,获取成功修改独占线程的状态,有人占用查看当前占有线程是否为当前线程,如果是则进行重入,此外还需要判断重入次数,如果超过了阈值,抛出错误/**

* 非公平锁

*/

static final class NonfairSync extends Sync {

private static final long serialVersionUID = 7316153563782823691L;

@Override

final void lock() {

/**

* 自旋获取资源,成功后修改独占状态,

* 失败后继续尝试获取,并将其加入到队列中以CLH自旋锁方式一直尝试获取资源

*/

if (compareAndSetState(0, 1)) {

setExclusiveOwnerThread(Thread.currentThread());

}

else {

//AQS内部方法,实则调用的是tryAcquire(),如果获取资源失败,则加入到AQS队列尾部,并且以自旋的方 //式一直尝试获取资源,不会响应中断,但是设置了终端标记,在获取到资源后会释放掉资源,并且将当前线程 //状态设置为CANCELLED,这一部分详细代码请看AQS源码解析

acquire(1);

}

}

/**

* 尝试获取资源

*/

@Override

protected final boolean tryAcquire(int acquires) {

return nonfairTryAcquire(acquires);

}

}

构造器/**

* 默认为非公平锁,相比于公平锁,性能更高,因为公平锁每次还需要查看AQS中是否有等待的线程

*/

public ReentrantLock() {

sync = new NonfairSync();

}

//也可以指定创建公平锁或非公平锁

public ReentrantLock(boolean fair) {

sync = fair ? new FairSync() : new NonfairSync();

}

案例之前某大厂的一个面试题,使用三个线程顺序打印出ABC三个字母,第一个线程打印A,然后第二个线程打印B,第三个线程打印C,打印10轮public class PrintWord {

private static ReentrantLock lock = new ReentrantLock();

static Condition conditionA = lock.newCondition();

static Condition conditionB = lock.newCondition();

static Condition conditionC = lock.newCondition();

private static int i = 1;

public static void main(String[] args) {

new Thread(() -> {

for (int j = 0; j < 10; j++) {

printA();

}

},"A").start();

new Thread(() -> {

for (int j = 0; j < 10; j++) {

printB();

}

},"B").start();

new Thread(() -> {

for (int j = 0; j < 10; j++) {

printC();

}

},"C").start();

}

private static void printA() {

lock.lock();

try {

if (i != 1) {

conditionA.await();

}

System.out.println(Thread.currentThread().getName());

i = 2;

conditionB.signal();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

}

private static void printB() {

lock.lock();

try {

if (i != 2) {

conditionB.await();

}

System.out.println(Thread.currentThread().getName());

i = 3;

conditionC.signal();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

}

private static void printC() {

lock.lock();

try {

if (i != 3) {

conditionC.await();

}

System.out.println(Thread.currentThread().getName());

i = 1;

conditionA.signal();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

}

}

Semaphore

一, 是什么?怎么用?

是什么?信号量,从概念上讲,信号量维护一组许可证,每个线程都可以来获取许可证,直至许可证为空

怎么用?可以使用其控制并发线程的数量

二, 类架构

1460000037518907从上面架构图我们可以看出,Semaphore底层也是使用的AQS,并且和ReentrantLock一样,都提供了公平式和非公平式获取资源

类的属性/**

* 实现信号量的同步器

*/

private final Sync sync;abstract static class Sync extends AbstractQueuedSynchronizer {

private static final long serialVersionUID = 1192457210091910933L;

/**

* 设置许可证的数量

*/

Sync(int permits) {

setState(permits);

}

/**

* 获取当前剩余的许可证数量

*/

final int getPermits() {

return getState();

}

/**

* 以共享的方式非公平获取许可证

*/

final int nonfairTryAcquireShared(int acquires) {

for (;;) {

//获取目前所剩余的许可证

int available = getState();

//计算获取之后剩余的许可证

int remaining = available - acquires;

//如果许可证数量为负就修改state值

if (remaining < 0 || compareAndSetState(available, remaining))

return remaining;

}

}

/**

* 以共享的方式释放许可证

*/

@Override

protected final boolean tryReleaseShared(int releases) {

for (;;) {

int current = getState();

int next = current + releases;

if (next < current) // overflow

throw new Error("Maximum permit count exceeded");

//CAS设置许可证数量

if (compareAndSetState(current, next))

return true;

}

}

/**

* 按照具体的数量减少许可证

*/

final void reducePermits(int reductions) {

for (;;) {

int current = getState();

int next = current - reductions;

if (next > current)

throw new Error("Permit count underflow");

if (compareAndSetState(current, next))

return;

}

}

/**

* 获取当前可以使用的许可证,如果等于0则直接修改state

*/

final int drainPermits() {

for (;;) {

int current = getState();

if (current == 0 || compareAndSetState(current, 0))

return current;

}

}

}

三, 具体实现

公平式获取许可证: 每次获取先都需要看AQS中是否有等待的线程,如果有,则直接退出,否则获取许可证,修改剩余许可证的数量,并且返回剩余许可证数量

释放许可证: 由AQS的releaseShared调用,释放许可证时,在原先的基础上加上释放的许可证,但是释放的数量不能为负,释放成功,调用AQS中的doReleaseShared方法,将队列头节点的状态设置为0然后从头节点的后继节点中找出一个状态值小于0的线程节点释放/**

* 公平式同步器

*/

static final class FairSync extends Sync {

private static final long serialVersionUID = 2014338818796000944L;

FairSync(int permits) {

super(permits);

}

/**

* 获取许可证,由AQS中的acquireShared方法调用,如果许可证数量小于0,

* 则将当前线程加入到队列中一直轮询尝试过去许可证

*/

@Override

protected int tryAcquireShared(int acquires) {

for (;;) {

//队列中有等待的线程,直接返回

if (hasQueuedPredecessors())

return -1;

int available = getState();

int remaining = available - acquires;

//获取许可证,CAS修改state值

if (remaining < 0 || compareAndSetState(available, remaining))

return remaining;

}

}

}

非公平式获取许可证: 上来直接尝试获取信号量,如果获取成功返回剩余许可证,如果许可证数量小于0则进入AQS队列中

释放许可证: 和非公平式相同/**

* 非公平方式下同步器

*/

static final class NonfairSync extends Sync {

private static final long serialVersionUID = -2694183684443567898L;

NonfairSync(int permits) {

super(permits);

}

/**

* 获取资源,这一步由AQS中的acquireShared调用,每次获取资源后会返回剩余的许可证,上面有写

* 如果小于等于0则当前线程会一直处于CLH锁中,如果大于0则会唤醒队列中所有状态为SIGNAL的线程

* 详情见AQS源码948行开始

*/

@Override

protected int tryAcquireShared(int acquires) {

return nonfairTryAcquireShared(acquires);

}

}

获取许可证public void acquire() throws InterruptedException {

sync.acquireSharedInterruptibly(1);

}

public void acquireUninterruptibly() {

sync.acquireShared(1);

}

释放许可证public void release() {

sync.releaseShared(1);

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/359750.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php怎么把字符转成大写,php怎么把字符串转换为大写

php把字符串转换为大写的方法&#xff1a;可以利用内置函数strtoupper()来进行转换。strtoupper()函数可以把指定的字符串转换为大写&#xff0c;并返回被转换为大写的字符串。使用函数&#xff1a;(学习视频推荐&#xff1a;php视频教程)strtoupper() 函数把字符串转换为大写&…

oracle存储过程与函数的区别及作用,Oracle存储过程与存储函数-入门

文章思维导图一. 存储过程和存储函数的定义定义&#xff1a;存储在数据库中&#xff0c;供所有用户程序调用的子程序叫做存储过程/存储函数。复杂点的解释&#xff1a;存储过程(Stored Procedure)&#xff0c;就是一组用于完成特定数据库功能的SQL 语句集&#xff0c;该SQL语句…

CC++初学者编程教程(8) VS2013配置编程助手与QT

1. 2. 配置编程助手 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19&#xff0e; 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30&#xff0e; 31. 32&#xff0e; 33. 34. 35. 36. 37. 38. 39 40 41 42 43 这个时候我们就在VS2013里面集成了QT&#xff0c;编程助…

转子碰磨 matlab,航空科普:什么是航空发动机转子碰磨?

中国航空报讯&#xff1a;随着人们对航空发动机油耗、推重比等要求的逐步提高&#xff0c;提升航空发动机运行效率&#xff0c;尤其是提升民用航空发动机的经济性已经变得越来越重要。航空发动机的总体运行效率是气动效率、燃烧效率、冷却效率与机械效率等共同决定的。其中&…

Android 自定义 ListView 显示网络上 JSON 格式歌曲列表

本文内容 环境 项目结构 演示自定义 ListView 显示网络上 JSON 歌曲列表 参考资料 本文最开始看的是一个国人翻译的文章&#xff0c;没有源代码可下载&#xff0c;根据文中提供的代码片段&#xff0c;自己新建的项目&#xff08;比较可恶的是&#xff0c;没有图标图片资源&…

oracle 索引invisible,Oracle index unusable和invisible的区别

invisible index会被优化器所忽略&#xff0c;但是dml操作仍然会维护索引。在session或者system级别使用参数OPTIMIZER_USE_INVISIBLE_INDEX摘录自Oracle 11g的官方文档&#xff1a;UNUSABLE Specify UNUSABLE to mark the index or index partition(s) or index subpartition(…

php16进制密钥签名对接支付,简单理解rsa的加密和签名-PHP实现

我们先动手在linux上生成一下rsaPs&#xff1a;openssl是一堆加密算法和安全协议的开源集合,像RSA,DES,MD5,RC4等等,都能在openssl里面找到源代码.用openssl指定生成test.key文件&#xff0c;其中包含公钥私钥&#xff0c;1024为生成密钥长度tbtb:~/mimi$ openssl genrsa -out …

java中字符和字节的转换_Java最佳实践–字符到字节和字节到字符的转换

java中字符和字节的转换在使用Java编程语言时&#xff0c;我们将继续讨论与建议的实践有关的系列文章&#xff0c;我们将讨论String性能调优。 特别是&#xff0c;我们将着重于使用默认编码时如何有效地处理字符到字节和字节到字符的转换。 本文总结了两种建议的自定义方法与两…

Android实现简单短信发送器

布局&#xff1a; <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:id"id/container"android:layout_width"match_parent"android:layout_heigh…

linux 查看文件哈希码,使用linux的sha1sum命令查看效验文件哈希值命令

服务器今天小编给大家分享的是使用linux的sha1sum命令查看效验文件哈希值命令&#xff0c;相信很多人都不太了解&#xff0c;为了让大家更加了解&#xff0c;所以给大家总结了以下内容&#xff0c;一起往下看吧。一定会有所收获的哦。sha1sum命令用于生成和校验文件的sha1值。它…

Java中的传值与传引用

曾经对java中传值还是传引用的问题十分困惑&#xff0c;而且也被问到过许多次&#xff0c;无论是面试&#xff0c;还是平时我们平时的讨论中。 在stackoverflow上面浏览时&#xff0c;无意中发现了跟这个问题有关的论题&#xff0c;于是深入地查看了&#xff0c;感觉获益匪浅&a…

java高性能序列化_Java最佳实践–高性能序列化

java高性能序列化在使用Java编程语言时&#xff0c;我们将继续讨论与建议的实践有关的系列文章&#xff0c;我们将讨论并演示如何将对象序列化用于高性能应用程序。 所有讨论的主题均基于用例&#xff0c;这些用例源于电信行业关键任务超高性能生产系统的开发。 在阅读本文的…

linux进程属主6,20150917 Linux进程查看与管理以及作业管理

第一、基础知识MBR引导--内核--内核程序--》协调其它程序一般内核运行在硬件之上&#xff0c;各应用也在硬件之前1)OS的基本功能&#xff1a;文件系统、网络功能、进程管理、内存管理、驱动程序、安全功能以上为通用目的设置的程序。&#xff0c;程序指令数据&#xff0c;程序运…

在javaweb中通过servlet类和普通类读取资源文件

javaweb有两种方式读取资源文件 在Servlet中读取&#xff0c;可以使用servletContext&#xff0c;servletContext可以拿到web所有的资源文件&#xff0c;然后随便读&#xff0c;但是这种方法不常用&#xff0c;尽量少在Servlet中读取资源文件 在普通Java类中&#xff08;DAO中&…

数学图形(1.43)贝壳形曲线与鱼形曲线

贝壳形曲线 #http://curvebank.calstatela.edu/naturalcurves/slide7.jpgvertices 12000t from 0 to (2*PI)r 10*(0.7 2.6*cos(t) 1.3*pow(sin(90*t), 3))x r*sin(t) y r*cos(t) 鱼形曲线 #http://www.2dcurves.com/quartic/quarticfi.html vertices 1000 t from 0 to…

linux下编译ios,为iOS安装OpenCV

一&#xff0c;要求CMake 2.8.8 或者更高Xcode 4.2 或者更新二&#xff0c;从Git库取得最新的 OpenCV使用 git 客户端从 http://github.com/itseez/opencv clone Open 库。在 max os x 下&#xff0c;可以通过以下命令行完成cd ~/git clone https://github.com/Itseez/opencv…

学习使用资源文件[11] - DLL 中的资源文件

本例将把一张 bmp 图片, 以资源文件的方式嵌入 dll, 然后再调用.第一步: 建一个 DLL 工程, 如图:然后保存, 我这里使用的名称都是默认的.第二步: 建一个资源原文件, 如图:编辑内容如下(路径中的文件一定要存在):img1 BITMAP "c:\temp\test.bmp"然后, 取个名(后缀须是…

linux 自动安装 yum,LINUX6安装YUM仓库和实现开机自动挂载

# LINUX6安装YUM仓库和实现开机自动挂载---------------------------------安装环境-------------------------------Redhat6镜像文件&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1DKH6adbrsipM-cTzH-wBgA 百度网盘 密码是hxcbVmware Workstation虚拟机工具&#x…

.NET Framework 4.0源代码

原文出处&#xff1a;http://blogs.microsoft.co.il/blogs/arik/archive/2010/07/12/step-into-net-framework-4-0-source-code.aspx 本文将向你展示如何配置Visual Studio 2010逐语句执行.NET Framework 4.0源代码。 为啥? 因为很酷。 因为能帮助调试。 你不仅能看到源代码…

linux blender骨骼绑定,在Linux系统中安装开源3D创建套件Blender的方法

Blender是免费的开源3D创建套件&#xff0c;它可在Linux系统中安装&#xff0c;可使用snap或flatpak命令安装Blender。简介Blender也称为开源3D建模软件&#xff0c;支持整个3D管道&#xff0c;建模&#xff0c;装配&#xff0c;动画&#xff0c;模拟&#xff0c;渲染&#xff…