锁就是一种状态,比如互斥锁:同一时间只能有一个线程拥有,可以使用一个整型值来标志当前的状态

  • 0:表示没有现成占有锁
  • 1:表示锁已经被占用

AbstractQueuedSynchronizer

  • 实现Java中锁的基础,提供了一系列模板方法,程序员可以继承该类作为内部类实现自定义同步语义。
  • 通过一个整型变量state维护锁的同步状态
  • 通过CAS保证同步状态state的修改是原子的

这个抽象类中使用了很多模板方法,在实现锁机制的时候可以调用这些模板方法:

模板方法里面调用了一些尚未实现、留给程序员实现的抽象方法:

独占式同步方法:

共享式同步方法:

独占式同步状态

同时只能有一个线程占有锁,独占式同步过程:

// 获取锁,可以在在lock方法中调用
public final void acquire(int arg) {
// tryAcquire方法需要根据锁的功能自行实现
// 获取锁失败的话执行addWaiter,Node.EXCLUSIVE表明当前node获取的是独占式的锁
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
} // 因为获取锁失败,将当前node加入queue的最后一个节点tail
private Node addWaiter(Node mode) {
// 新建的node是独占模式,不对waitStatus赋值,也就是默认为0
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
// 如果队列不为空,则插入tail
if (pred != null) {
node.prev = pred;
// 因为是独占式的锁,只会有一个线程修改tail,不要循环
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果tail == null表示队列为空,调用enq入队
enq(node);
return node;
} private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 因为在这个时候可能其他线程已经入队,队列可能不再为null
// 如果依然为空,则初始化队列——新建一个node,并设为head=tail=newNode
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 如果其他线程已经入过队,则正常设置tail
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
} // 入队列之后进入自旋状态
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 不断循环尝试获取锁——自旋
for (;;) {
final Node p = node.predecessor(); // 获取前一个node
// 如果前一个node是head,则尝试获取锁,因为如果前一个是head说明head有可能已经释放锁,当前node可以尝试获取锁
if (p == head && tryAcquire(arg)) {
// 如果获取成功则将当前node设为head
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 判断是否需要park
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
} // 这个方法可以结合释放锁release(释放独占锁)和releaseShared(释放共享锁)来看,因为释放锁的时候会判断是否进行unpark操作
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
// 前面一个节点是signal,表示前面一个节点释放锁的时候(release)一定会unpark,那么当前node可以park(因为一定会被unpark,保证不会被永久阻塞)
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
// 把已经是cancelled的node从队列中移除,不再进行获取锁
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}

释放独占锁:

public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
// 独占锁的node.waitStatus默认是0,因为在初始化的时候不会对waitStatus赋值,如果不等于0,表示在加入队列的时候设置过waitStatus,设置为SIGNAL
if (h != null && h.waitStatus != 0)
// 如果head的waitStatus是SIGNAL表示要唤醒阻塞的线程
unparkSuccessor(h);
return true;
}
return false;
}

共享式同步状态

可以同时有多个线程获得锁

重入锁

同一个线程重复获得锁

读写锁

读锁:共享锁

写锁:独占锁

LockSupport

调用Unsafe的方法,负责阻塞或者唤醒线程。相当于一个线程有一个许可,默认是被占用的,park会占用这个许可,unpark会分配这个许可,只要调用过至少一次unpark,那么再调用一次park线程会返回,不会被永久阻塞:

  • 如果先调用park并且后面不会调用unpark,那么线程会被一直阻塞;
  • 如果先调用unpark(至少一次),再调用park,那么线程立即返回,不会阻塞
  • 如果先调用park(至少一次),再调用unpark,那么线程在调用unpark后返回,不会继续阻塞

所以unpark会给当前线程分配一个许可,如果之前调用过一次park或者后面调用一次park,那么线程不再继续阻塞,即:

  • 一个线程只有一个许可
  • unpark分配许可
  • park占用许可
  • 只有调用unpark的次数 >= 调用park的次数,线程才不会被永久阻塞

Condition

任意一个Java对象,都拥有一组Monitor方法(定义在Object上),包括:wait,notify等,与synchronized配合实现等待/通知模式

Lock和Condition结合也可以实现等待/通知模式

Java 线程 — AbstractQueuedSynchronizer的更多相关文章

  1. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  2. Java线程问题分析定位

    Java线程问题分析定位 分析步骤: 1.使用top命令查看系统资源占用情况,发现Java进程占用大量CPU资源,PID为11572: 2.显示进程详细列表命令:ps -mp 11572 -o THR ...

  3. 怎样分析java线程堆栈日志

    注: 该文章的原文是由 Tae Jin Gu 编写,原文地址为 How to Analyze Java Thread Dumps 当有障碍,或者是一个基于 JAVA 的 WEB 应用运行的比预期慢的时 ...

  4. Java 线程池框架核心代码分析

    前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过 ...

  5. Java线程同步之一--AQS

    Java线程同步之一--AQS 线程同步是指两个并发执行的线程在同一时间不同时执行某一部分的程序.同步问题在生活中也很常见,就比如在麦当劳点餐,假设只有一个服务员能够提供点餐服务.每个服务员在同一时刻 ...

  6. Java线程池使用和分析(二) - execute()原理

    相关文章目录: Java线程池使用和分析(一) Java线程池使用和分析(二) - execute()原理 execute()是 java.util.concurrent.Executor接口中唯一的 ...

  7. Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析

    1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...

  8. 并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)

    史上最清晰的线程池源码分析 鼎鼎大名的线程池.不需要多说!!!!! 这篇博客深入分析 Java 中线程池的实现. 总览 下图是 java 线程池几个相关类的继承结构:    先简单说说这个继承结构,E ...

  9. Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

随机推荐

  1. oracle中用户删除不了,ORA-01940提示 “无法删除当前已连接用户”

    Oracle删除用户的提示无法删除当前已连接用户两种解决方法如下: 1.先锁定用户.然后查询进程号,最后删除对应的进程.在删除对应的用户 SQL>alter user XXX account l ...

  2. [Python] Pitfalls: About Default Parameter Values in Functions

    Today an interesting bug (pitfall) is found when I was trying debug someone's code. There is a funct ...

  3. [UE4]自定义MovementComponent组件

    自定义Movement组件 目的:实现自定义轨迹如抛物线,线性,定点等运动方式,作为组件控制绑定对象的运动. 基类:UMovementComponent 过程: 1.创建UCustomMovement ...

  4. [转]Android开发:Parallax效果的ScrollerView,改编自ParallaxListView

    https://github.com/nirhart/ParallaxScroll这个gethub上的地址 本文转自http://www.2cto.com/kf/201502/376970.html ...

  5. node安装笔记

    安装node.js1.下载node可以直接下载二进制,也可以下载源代码再安装.我选择下载二进制: https://nodejs.org/dist/v4.6.0/node-v4.6.0-linux-x6 ...

  6. java的 new 关键字

    java的new关键字想必大家都知道这是实例化一个对象.没错,也是为新对象分配内存空间. 比如new MyDate(22,7,1964)这样一个案例,他的完成需要四部: 一.为新对象分配内存空间,将M ...

  7. HTML5-表单的创建

    <!-- 文本-->, <!-- 复选框-->,<!-- 单选按钮-->, <!-- 下拉菜单,select:选择,option:选项--> <! ...

  8. JAVA缓存技术

    介绍 JNotify:http://jnotify.sourceforge.net/,通过JNI技术,让Java代码可以实时的监控制定文件夹内文件的变动信息,支持Linux/Windows/MacOS ...

  9. PHP版微信公共平台消息主动推送,突破订阅号一天只能发送一条信息限制

    2013年10月06日最新整理. PHP版微信公共平台消息主动推送,突破订阅号一天只能发送一条信息限制 微信公共平台消息主动推送接口一直是腾讯的私用接口,相信很多朋友都非常想要用到这个功能. 通过学习 ...

  10. hander消息机制原理

    基本原理 线程中调用Handler.sendMsg()方法(参数是Message对象),将需要Main线程处理的事件 添加到Main线程的MessageQueue中,Main线程通过MainLoope ...