博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java中的队列同步器AQS -- AbstractQueuedSynchronizer
阅读量:6898 次
发布时间:2019-06-27

本文共 4912 字,大约阅读时间需要 16 分钟。

hot3.png

1)原理:

1)使用一个int成员变量表示同步状态(private volatile int state;),通过同步队列(一个FIFO双向队列)来完成同步状态的管理(即:线程的排队)。2)当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入到同步队列的尾部,并且当前线程(因进行自旋操作)被阻塞。3)首节点是成功获取到同步状态的节点,首节点的线程在释放同步状态时,将会唤醒后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点。	同步队列中的节点(Node):用来保存获取同步状态失败的线程引用、等待状态以及前驱和后继节点。

2)说明:

1)AQS是用来构建锁或者其它同步工具的基础框架,锁和同步工具都采用封装一个AQS的子类(静态内部类)的方式来实现其功能。2)同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,3)通过getState()、setState(int newState)和compareAndSetState(int expect,int update)来安全地修改状态。

3)同步器中的方法:

1)同步器中可以被子类重写的方法:	protected boolean tryAcquire(int arg) // 独占式获取同步状态	protected boolean tryRelease(int arg) // 独占式释放同步状态	protected int tryAcquireShared(int arg) // 共享式获取同步状态,当返回值大于等于0时,表示获取成功。	protected boolean tryReleaseShared(int arg) // 共享式释放同步状态	protected boolean isHeldExclusively() // 当前同步器是否在独占模式下被占用。		2)模板方法:	1>独占式操作同步状态的方法:		public final void acquire(int arg) 			独占式获取同步状态,如果当前线程成功获取同步状态,则该方法返回;否则,当前线程会进入同步队列等待。			该方法将会调用子类重写的tryAcquire(int arg)方法;如果后续对线程进行中断操作,线程不会从同步队列中移出。				public final void acquireInterruptibly(int arg) throws InterruptedException {			在acquire(int arg)的基础上增加了中断响应。			若当前线程未获取到同步状态,进入同步队列等待时,如果当前线程被中断,则该方法抛出终端异常并返回。					public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException			在acquireInterruptibly(int arg)的基础上增加了超时限制。			在指定的时间内,获取到同步状态返回true,否则返回false。					public final boolean release(int arg)			独占式释放同步状态,该方法会在释放同步状态之后,会唤醒其后继节点(即:将同步队列中第一个节点包含的线程唤醒)。			该方法将会调用子类重写的tryRelease(int arg)方法;	2>共享式操作同步状态的方法:		public final void acquireShared(int arg)			共享式的获取同步状态,如果当前线程成功获取同步状态,则该方法返回;否则,当前线程会进入同步队列等待。			该方法将会调用子类重写的tryAcquireShared(int arg)方法。tryAcquireShared(int arg)方法返回值为int类型,当返回值大于等于0时,表示能够获取到同步状态。			因此,在共享式获取的自旋过程中,成功获取到同步状态并退出自旋的条件就是tryAcquireShared(int arg)方法返回值大于等于0。						与独占式获取的区别:在同一个时刻,可以有多个线程同时获取到同步状态。				public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {				在acquireShared(int arg)的基础上增加了超时限制。					public final boolean releaseShared(int arg)			共享式释放同步状态			该方法将会调用子类重写的tryReleaseShared(int arg)方法;			tryReleaseShared(int arg)方法必须确保同步状态的安全释放(一般是通过循环和CAS来保证同步状态的安全释放)。			3)方法源码分析:	独占式:	1)获取同步状态的方法:		public final void acquire(int arg) {			if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))				selfInterrupt();		}				final boolean acquireQueued(final Node node, int arg) {			boolean failed = true;			try {				boolean interrupted = false;				for (;;) { // 自旋					final Node p = node.predecessor();					// 1移出队列(或停止自旋)的条件是前驱节点为头节点且成功获取了同步状态。					// 2当前驱节点是头节点时,才会去尝试获取同步状态					if (p == head && tryAcquire(arg)) { 						setHead(node);						p.next = null; // help GC						failed = false;						return interrupted;					}					if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) // 进入等待状态并检查时是否被中断						interrupted = true;				}			} finally {				if (failed)					cancelAcquire(node);			}		}				1>调用自定义同步器实现的tryAcquire(int arg)方法,该方法保证线程安全的获取同步状态。		2>如果同步状态获取失败,则构造独占式(Node.EXCLUSIVE)同步节点并通过addWaiter(Node node)方法将该节点加入到同步队列的尾部。		3>最后调用acquireQueued(Node node,int arg)方法,使得该节点以自旋的方式获取同步状态。		4>如果获取不到同步状态,则阻塞节点中的线程。		5>被阻塞线程的唤醒主要依靠前驱节点的出队或阻塞线程被中断来实现。			2)释放同步状态的方法:		public final boolean release(int arg) {			if (tryRelease(arg)) {				Node h = head;				if (h != null && h.waitStatus != 0)					unparkSuccessor(h);				return true;			}			return false;		}				共享式:	1)获取同步状态的方法:		public final void acquireShared(int arg) {			if (tryAcquireShared(arg) < 0)				doAcquireShared(arg);		}				private void doAcquireShared(int arg) {			final Node node = addWaiter(Node.SHARED);	// 则构造共享式(Node.SHARED)同步节点,并将该节点加入到同步队列的尾部			boolean failed = true;			try {				boolean interrupted = false;				for (;;) { // 自旋					final Node p = node.predecessor();					if (p == head) { 					// 当前驱节点是头节点时,才会去尝试获取同步状态						int r = tryAcquireShared(arg);							if (r >= 0) {					// 当返回值大于等于0时,表示获取同步状态成功并从退出自旋							setHeadAndPropagate(node, r);							p.next = null; // help GC							if (interrupted) selfInterrupt();							failed = false;							return;						}					}					if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())						interrupted = true;				}			} finally {				if (failed)					cancelAcquire(node);			}		}		2)释放同步状态的方法:		public final boolean releaseShared(int arg) {			if (tryReleaseShared(arg)) {				doReleaseShared();				return true;			}			return false;		}		private void doReleaseShared() {			for (;;) {	// 通过循环和CAS来保证同步状态的安全释放				Node h = head;				if (h != null && h != tail) {					int ws = h.waitStatus;					if (ws == Node.SIGNAL) {						if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))							continue;            // loop to recheck cases						unparkSuccessor(h);					}					else if (ws == 0 &&							 !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))						continue;                // loop on failed CAS				}				if (h == head)                   // loop if head changed					break;			}		}

参考书籍:<java并发编程的艺术>

转载于:https://my.oschina.net/u/1399755/blog/1555091

你可能感兴趣的文章
JavaScript:利用递归实现对象深拷贝
查看>>
PHP代码自动检测(git/svn集成PHP_CodeSniffer)
查看>>
领域驱动设计实战案例(五):订单上下文POCO模型
查看>>
159. Longest Substring with At Most Two Distinct Characters
查看>>
利用PHP实现常用的数据结构之二叉树(小白系列文章五)
查看>>
Intellij Idea + spring-boot + thymleaf实现热部署
查看>>
js--string/正则表达式replace方法详解
查看>>
基于Vue2实现的仿手机QQapp(支持对话功能,滑动删除....)—— 聊聊开发过程中踩到的一些坑与解决方案,以及个人感悟...
查看>>
(三)java多线程之wait notify notifyAll
查看>>
[面经] [实习] Next Capital 实习 Summer 2017 OA
查看>>
那家CTO带头喊麦的直播公司,快要倒闭了
查看>>
想像亚马逊或 Netflix 一样酷?抱走敏捷转型五大秘籍
查看>>
揭秘码云:全球第二大代码托管平台的核心架构
查看>>
V8十年故事:从农场诞生的星球最强JS引擎
查看>>
AI一周热闻:周志华获IEEE技术成就奖;英伟达发布最小AI计算机
查看>>
有赞透明多级缓存解决方案(TMC)设计思路
查看>>
使用Prometheus和Grafana实现SLO
查看>>
堆和栈
查看>>
Spotify希望自己擅于失败
查看>>
隐私和安全是macOS Mojave和Safari 12的第一要务
查看>>