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 ...
随机推荐
- Mac OS下SVN的使用:服务的和客户端
在Windows环境中,我们一般使用TortoiseSVN来搭建svn环境.在Mac环境下,由于Mac自带了svn的服务器端和客户端功能,所以我们可以在不装任何第三方软件的前提下使用svn功能,不过还 ...
- XML的序列化与反序列化
开发时会把数据持久化成xml格式,当然可以用xmlwriter来实现,不过感觉不方便,而且很繁琐.推荐使用直接序列化.反序列化对象的方法来处理. 直接上代码: public static class ...
- Java之IO转换流
直接上码: 读取键盘录入数据代码演示: import java.io.IOException; import java.io.InputStream; /** *读取键盘录入的数据,并打印在控 ...
- 快速构建Windows 8风格应用14-ShareContract概述及原理
原文:快速构建Windows 8风格应用14-ShareContract概述及原理 本篇博文主要介绍Share Contract概述.Share Contract实现原理.实现Share Contra ...
- 对于发Github的contributions贡献不会增加
最近发现每天在 Github 做代码提交,可是 contributions 的面板(贡献图)上的绿点(即贡献值)却没有增长了.擦~ 有两个礼拜了. 例如以下图并且.同一时候发现曾经的绿点也是稀稀拉拉的 ...
- robin 今日南
我很高兴,在学校体育馆看到李彦宏博士. 这是第一个真正的一次在媒体上看到,只能看到人才足够多的人,现实,我觉得非常好. 我不是一个真正罗宾的粉丝.百度是不是很热衷于这家公司.,但现在我仍然兴奋,我会被 ...
- 开源文档管理系统LogicalDOC测试报告---安装篇
开源文档管理系统LogicalDOC测试报告---安装篇 分类: Linux2011-06-22 15:40 7436人阅读 评论(3) 收藏 举报 文档管理测试mysql数据库installerja ...
- SpringMVC格式化显示
SpringMVC学习系列(7) 之 格式化显示 在系列(6)中我们介绍了如何验证提交的数据的正确性,当数据验证通过后就会被我们保存起来.保存的数据会用于以后的展示,这才是保存的价值.那么在展示的时候 ...
- ASP.NET WebApi 入门
今天参照微软官方(http://www.asp.net)学习了WebApi,在这里摘录如下: 前言 HTTP 不只是为了生成 web 页面.它也是一个强大的平台,可以建设公开服务和数据的 Api.HT ...
- 后台XML处理
public void GetInfo() { string message = @"<?xml version='1.0' encoding='utf-8' ...