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

线程同步是指两个并发执行的线程在同一时间不同时执行某一部分的程序。同步问题在生活中也很常见,就比如在麦当劳点餐,假设只有一个服务员能够提供点餐服务。每个服务员在同一时刻只能接待一个顾客的点餐,那么除了正在接待的顾客,其他人只能等待排队。当一个点餐服务完成之后,其他顾客就可以上去进行点餐。
从这个例子中可以看到如下几个关注点:
- 点餐服务为临界区域(critical area),其可同时进行的数量,即为有多少人可进入临界区域。
- 排队即为对目前暂时无法取得点餐服务的人的一种处理方式。这种处理方式的特性有公平性(按次序),效率性(接手最快为最好)等。
- 顾客进行排队和从队伍中叫一个顾客来进行服务即为睡眠(park)和唤醒(unpark)机制。
并发中线程同步是重点需关注的问题,线程同步自然也有一定的模式,DougLea就写出了一个简单的框架AQS用来支持一大类线程同步工具,如ReentrantLock,CountdownLatch,Semphaore等。
AQS是concurrent包中的一系列同步工具的基础实现,其提供了状态位,线程阻塞-唤醒方法,CAS操作。基本原理就是根据状态位来控制线程的入队阻塞、出队唤醒来解决同步问题。

入队:

出队:

二、代码分析
下面以ReentrantLock来说明AQS的组成构件的工作情况:
在ReentrantLock中封装了一个同步器Sync,继承了AbstractQueuedSynchronizer,根据对临界区的访问的公平性要求不同,又分为NonfairSync和FairSync。为了简化起见,就取最简单的NonFairSync作为例子来说明:
1. 对于临界区的控制:
java.util.concurrent.locks.ReentrantLock.NonfairSync
1: final void lock() {
2:
3: if (compareAndSetState(0, 1))
4:
5: setExclusiveOwnerThread(Thread.currentThread());
6:
7: else
8:
9: acquire(1);
10:
11: }
12:
从以上代码可以看出,其主要目的是采用cas比较临界区的状态。
1.1. 如果为0,将其设置为1,并记录当前线程(当前线程可进入临界区);
1.2. 如果为1,尝试获取临界区控制
java.util.concurrent.locks.AbstractQueuedSynchronizer
1: public final void acquire(int arg) {
2:
3: if (!tryAcquire(arg) &&
4:
5: acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
6:
7: selfInterrupt();
8:
9: }
10:
1.2.1. NonFairLock的tryAcquire实现为:
1: final boolean nonfairTryAcquire(int acquires) {
2:
3: final Thread current = Thread.currentThread();
4:
5: int c = getState();
6:
7: if (c == 0) {
8:
9: if (compareAndSetState(0, acquires)) {
10:
11: setExclusiveOwnerThread(current);
12:
13: return true;
14:
15: }
16:
17: }
18:
19: else if (current == getExclusiveOwnerThread()) {
20:
21: int nextc = c + acquires;
22:
23: if (nextc < 0) // overflow
24:
25: throw new Error("Maximum lock count exceeded");
26:
27: setState(nextc);
28:
29: return true;
30:
31: }
32:
33: return false;
34:
35: }
36:
上述代码主要是针对大部分线程进入临界区工作时间不会很长而进行的性能优化,第一次尝试失败了,极有可能过一会儿锁就释放了,因此重新去尝试获取锁。
1.2.2. 以下这段代码是锁的精华部分
java.util.concurrent.locks.AbstractQueuedSynchronizer
1: final boolean acquireQueued(final Node node, int arg) {
2:
3: try {
4:
5: boolean interrupted = false;
6:
7: for (;;) {
8:
9: final Node p = node.predecessor();
10:
11: if (p == head && tryAcquire(arg)) {
12:
13: setHead(node);
14:
15: p.next = null; // help GC
16:
17: return interrupted;
18:
19: }
20:
21: if (shouldParkAfterFailedAcquire(p, node) &&
22:
23: parkAndCheckInterrupt())
24:
25: interrupted = true;
26:
27: }
28:
29: } catch (RuntimeException ex) {
30:
31: cancelAcquire(node);
32:
33: throw ex;
34:
35: }
36:
37: }
38:
在无限循环中完成了对线程的阻塞和唤醒。阻塞在parkAndCheckInterrupt()唤醒后从此处进行释放。
算法过程:
- 从加入队列的node开始反向查找,将前一个元素赋值给p;
- 如果p是head,那么试着再获得一次锁tryAcquire(arg),成功则将head指针往后移动,并跳出循环;
- 如果上一步骤尝试失败,那么进行测试是否要park ,如果状态为0,将其标记为SIGNAL,并返回false;
- 再重复检查一次,发现其头部的waitStatus为-1.Node.signal。确认需要park successor; 进行parkAndCheckInterrupt()将当前线程阻塞。
2. 对于临界区的释放
2.1. java.util.concurrent.locks.AbstractQueuedSynchronizer
1: public final boolean release(int arg) {
2:
3: if (tryRelease(arg)) {
4:
5: Node h = head;
6:
7: if (h != null && h.waitStatus != 0)
8:
9: unparkSuccessor(h);
10:
11: return true;
12:
13: }
14:
15: return false;
16:
17: }
18:
2.1.1. java.util.concurrent.locks.ReentrantLock.Sync
1: protected final boolean tryRelease(int releases) {
2:
3: int c = getState() - releases;
4:
5: if (Thread.currentThread() != getExclusiveOwnerThread())
6:
7: throw new IllegalMonitorStateException();
8:
9: boolean free = false;
10:
11: if (c == 0) {
12:
13: free = true;
14:
15: setExclusiveOwnerThread(null);
16:
17: }
18:
19: setState(c);
20:
21: return free;
22:
23: }
24:
将state进行变化-releases,检查当前线程是否是拿住锁的线程,否则掷出异常.如果为0,将持有锁线程标记为null。
从ReentrantLock例子可以看出AQS的工作原理,更为精妙的是,在这几个基本机制作用下衍生了许多种并发工具,以后的介绍中可以看到。
Java线程同步之一--AQS的更多相关文章
- java 线程同步 原理 sleep和wait区别
java线程同步的原理java会为每个Object对象分配一个monitor, 当某个对象(实例)的同步方法(synchronized methods)被多个线程调用时,该对象的monitor将负责处 ...
- Java线程同步_1
Java线程同步_1 synchronized 该同步机制的的核心是同步监视器,任何对象都可以作为同步监视器,代码执行结束,或者程序调用了同步监视器的wait方法会导致释放同步监视器 synchron ...
- java线程 同步临界区:thinking in java4 21.3.5
java线程 同步临界区:thinking in java4 21.3.5 thinking in java 4免费下载:http://download.csdn.net/detail/liangru ...
- JAVA - 线程同步和线程调度的相关方法
JAVA - 线程同步和线程调度的相关方法 wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁:wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等 ...
- Java线程同步的四种方式详解(建议收藏)
Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...
- Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析
1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...
- Java线程同步类容器和并发容器(四)
同步类容器都是线程安全的,在某些场景下,需要枷锁保护符合操作,最经典ConcurrentModifiicationException,原因是当容器迭代的过程中,被并发的修改了内容. for (Iter ...
- Java线程同步和线程通信
一.线程同步 当多个线程访问同一个数据时,非常容易出现线程安全问题.这时候就需要用线程同步. 不可变类总是线程安全的,因为它的对象状态是不可改变的,但可变类对象需要额外的方法来保证线程安全. 1.同步 ...
- 【总结】Java线程同步机制深刻阐述
原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread ...
随机推荐
- Java清理临时目录文件Demo(一)
/** * 删除单个文件 * * @param sPath * 被删除文件的文件名 * @return 单个文件删除成功返回true,否则返回false */ public static boolea ...
- C# ASP.NET CSV文件导入数据库
原文:C# ASP.NET CSV文件导入数据库 using System; using System.Collections.Generic; using System.Text; using Sy ...
- hdu oj1102 Constructing Roads(最小生成树)
Constructing Roads Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...
- PO Box简介
使用Erlang写程序的时候,经常会碰到一种情况:因为Erlang进程的mailbox是没有大小限制的,所以它会一直接受消息,直到Erlang节点内存溢出.在大多数情况下,我们可以通过限制消息生产者的 ...
- leetcode第30题--Next Permutation
problem: Implement next permutation, which rearranges numbers into the lexicographically next greate ...
- MVC无刷新分页
MVC无刷新分页(即局部刷新,带搜索,页数选择,排序功能) 我查看了很多网站,大部分评论分页都是局部刷新的,可大部分电商商品展示分页都是有刷新页面的,于是我便做了一个商品展示无刷新分页的例子.接下 ...
- JavaScipt中对DOM的理解
一.理解DOM 浏览器通过文档对象模型DOM使JavaScript程序可以访问页面上的元素,而DOM是页面上XHTML中文档正文标题.段落.列表.样式ID.class以及所有其他出现的数据的一个内部表 ...
- php-fpm介绍及配置
php-fpm是什么 全称是php fastcgi process manager即php fastcgi进程管理器,相比fastcgi静态的唤起cgi,fpm能根据访问的压力动态的唤起cgi进程和销 ...
- 经典的SQL语句面试题
Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,Tname) 教师表 问题 ...
- 简话ASP.NET Web API
简话ASP.NET Web API 在vs2012中,我们很容易在根据选择的ASP.NET MVC Web应用程序来新建一个Web API应用,聪明的你一定想见得到,Web API和MVC有着某种联系 ...