AQS的数据结构及实现原理
|
int waitStatus
|
等待状态:
1 ,在队列中等待的线程等待超时或者被中断,从队列中取消等待;
-1,后继节点处于等待;
-2,节点在等待队列中,当condition被signal()后,会从等待队列转到同步队列;
-3,表示下一次共享式同步状态获取将会被无条件传播下去;
0,初始状态
|
|
Node prev
|
前驱节点
|
|
Node next
|
后继节点
|
|
Node nextWaiter
|
等待队列中的后继节点,如果当前节点是共享的,则这个字段将是一个SHARED常量,也就是说节点类型(独占或共享)和等待队列中的后继节点共用同一字段
|
|
Thread thread
|
获取同步状态的线程
|


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(); //获取当前节点的前节点
if (p == head && tryAcquire(arg)) { // 前节点为head,并且自己获取同步成功,说明前节点线程已经执行结束
setHead(node); //既然前节点的线程结束了,那就把自己设为head节点
p.next = null; // help GC //断开前节点,便于回收
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //这里会将线程挂起
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); //唤醒后继节点的操作在这里
return true;
}
return false;
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) //小于0,说明没获取成功
doAcquireShared(arg); //继续获取
}
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED); //节点类型为共享类型
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); //尾节点的前一个节点(当前节点就是尾节点)
if (p == head) { //前节点是head,则再次获取锁
int r = tryAcquireShared(arg); // 这个tryAcquireShared()的返回值是共享资源的剩余量,就是还可以允许访问的线程数
if (r >= 0) { //如果获取到了锁,进行相关设置
setHeadAndPropagate(node, r); // 进行head节点替换,并且如果剩余量有剩余,则继续往下传递
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //没获取到锁,则将线程挂起
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE); //节点类型是独占式的
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { //前节点为head,则尝试获取锁
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime(); // 计算剩余时间
if (nanosTimeout <= 0L) // 剩余时间已经到了
return false;
if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) //没获取到锁,时间也没到,则挂起一小段时间。注意如果时间剩余非常小了,比spinForTimeoutThreshold还小,则不挂起了,直接死循环一小会儿,进行获取锁
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
public class TwinsLock implements Lock {
private final Sync sync ;
private static final class Sync extends AbstractQueuedSynchronizer{
Sync(int count){
if(count <= 0){
throw new IllegalArgumentException("count must large than zero .");
}
setState(count); // 这里可以看到,count就是可重入的线程数
}
public int tryAcquireShared(int reduceCount){
for(;;){
int current = getState();
int newCount = current - reduceCount;
if(newCount < 0 || compareAndSetState(current, newCount)){//能把数量减掉并设置,就相当于获取锁成功
return newCount;
}
}
}
public boolean tryReleaseShared(int returnCount){
for(;;){
int current = getState();
int newCount = current + returnCount;
if(compareAndSetState(current, newCount)){
return true;
}
}
}
}
public TwinsLock (int count){
this.sync = new Sync(count);
}
@Override
public void lock() {
sync.acquireShared(1);
}
@Override
public void unlock() {
sync.releaseShared(1);
}
// 其它方法
}
public class SharedTest {
private int count = 0;
private final TwinsLock twinsLock = new TwinsLock(1);
@Test
public void test(){
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
MyThread mt4 = new MyThread();
MyThread mt5 = new MyThread();
mt1.start();
mt2.start();
mt3.start();
mt4.start();
mt5.start();
try {
mt1.join();
mt2.join();
mt3.join();
mt4.join();
mt5.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终结果:" + count);
}
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<1000;i++){
twinsLock.lock();
try {
count = count + 1;
}catch (Exception e){
System.out.println("异常啦 ~ ~ " +e.getMessage());
}finally {
twinsLock.unlock();
}
}
}
}
}

AQS的数据结构及实现原理的更多相关文章
- Java 中队列同步器 AQS(AbstractQueuedSynchronizer)实现原理
前言 在 Java 中通过锁来控制多个线程对共享资源的访问,使用 Java 编程语言开发的朋友都知道,可以通过 synchronized 关键字来实现锁的功能,它可以隐式的获取锁,也就是说我们使用该关 ...
- MySQL索引背后的数据结构及算法原理【转】
本文来自:张洋的MySQL索引背后的数据结构及算法原理 摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持 ...
- MySQL 索引背后的数据结构及算法原理
本文转载自http://blog.jobbole.com/24006/ 摘要本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引 ...
- MySQL索引背后的数据结构及算法原理 (转)
摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...
- MySQL(二)索引背后的数据结构及算法原理
本文转载自CodingLabs,原文链接 MySQL索引背后的数据结构及算法原理 目录 摘要 一.数据结构及算法基础 1. 索引的本质 2. B-Tree和B+Tree 3. 为什么使用B-Tree( ...
- CodingLabs - MySQL索引背后的数据结构及算法原理
原文:CodingLabs - MySQL索引背后的数据结构及算法原理 首页 | 标签 | 关于我 | +订阅 | 微博 MySQL索引背后的数据结构及算法原理 作者 张洋 | 发布于 2011-10 ...
- MySQL索引之数据结构及算法原理
MySQL索引之数据结构及算法原理 MySQL支持多个存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等.本文只关注BTre ...
- 一文搞懂AQS及其组件的核心原理
@ 目录 前言 AbstractQueuedSynchronizer Lock ReentrantLock 加锁 非公平锁/公平锁 lock tryAcquire addWaiter acquireQ ...
- 【转】MySQL索引背后的数据结构及算法原理
摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...
随机推荐
- 关于PHP中的Trait
今天看PHP框架,看到Trait部分.没见过,好奇查了一下. PHP手册说的是解决多继承的问题.但是一般面向对象的语言中,解决多继承都是通过接口,PHP也有接口.貌似看上去Trait和Interfac ...
- javascript webstorm用法
javascript webstorm用法 一.什么是webstorm? WebStorm 是jetbrains公司旗下一款JavaScript 开发工具.被广大中国JS开发者誉为“We ...
- 13本热门书籍免费送!(Python、SpingBoot、Entity Framework、Ionic、MySQL、深度学习、小程序开发等)
七月第一周,网易云社区联合清华大学出版社为大家送出13本数据分析以及移动开发的书籍(Python.SpingBoot.Entity Framework.Ionic.MySQL.深度学习.小程序开发等) ...
- CENTOS7 使用 Nginx + Uwsgi 部署 Django 项目
写在前面的话 最近总是见到有新学 Django 的朋友在部署自己的项目到 Linux 上面的时候运行不起来,所以就动手写了这篇博客. 对于不会搭建 Python 3 环境的朋友可以参考前面的博客[CE ...
- hbase0.92 数据修复
现有集群环境:8台region server.1台master server 早晨重启某一region server后,该server的region全部下线未恢复. 重启前 hbase(main):0 ...
- SHELL编程规范与变量
shell相比标准开发语言比如JAVA,C,C++来说还是比较简单的,其实就是一堆命令的集合而已,初学者从简单的shell脚本学起,至于shell编程还是要有编程思想在里面,因此,循序渐进很重要,先研 ...
- selenium滑动验证码操作
1.首先要找到你要滑动的地方 2.调动鼠标事件按住不动 3.调整坐标即可 我这里是为了调试加了很多的sleep,print(hander)是为了看是否定位到了元素 4.效果如下图,但是我这里的验证文字 ...
- SDUT OJ 数据结构实验之串一:KMP简单应用 && 浅谈对看毛片算法的理解
数据结构实验之串一:KMP简单应用 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Descr ...
- mysql自动备份并上传至git仓库
自动备份 备份需求 数据库备份的重要性再怎么强调也不为过.当你的操作出现差错,但又因为没有作备份导致数据无法还原时,你就能体会到“万念俱灰”的心情了. 数据库备份有多种形式,本文以我的个人网站数据 ...
- [HNOI2004]树的计数 BZOJ 1211 prufer序列
题目描述 输入输出格式 输入格式: 输入文件第一行是一个正整数n,表示树有n个结点.第二行有n个数,第i个数表示di,即树的第i个结点的度数.其中1<=n<=150,输入数据保证满足条件的 ...