AQS是用来构建锁或者其它同步组件的基础框架,它使用一个int变量来表示同步状态,通过内置的FIFO队列来完成获取线程的排队工作,concurrent包的作者Doug Lea期望它能称为实现大部分同步需求的基础。
  同步器的使用方式是继承,子类通过继承AQS并实现它的相关方法来管理同步状态,在子类方法的实现中不可避免的要对状态进行更改,AQS提供了3个方法:getState()、setState(int newState)跟compareAndSetState(int expect, int update)来进行操作,他们能保证状态的改变是安全的。子类被推荐定义为同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅定义了若干同步状态获取和释放的方法来供自定义同步组件使用。同步器支持同步状态的独占获取或者共享获取,以方便实现不同类型的同步组件。
  同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。可以这样理解两者之间的关系:锁是面向使用者的,它定义了使用者与锁交互的接口,隐藏了实现细节;同步器是面向锁的,它简化了锁的实现方式,屏蔽了同步状态管理,线程的排队、等待与唤醒等底层操作。锁和同步器很好的隔离了使用者跟实现者所需要关注的领域。
AQS的相关API:
  同步器提供的可重写的方法:
方法名称
描述
boolean tryAcquire(int arg)
独占式获取同步状态。实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行cas设置同步状态。(因为可能多个线程都在获取)
boolean tryRelease(int arg)
独占式释放同步状态,成功返回true,失败返回false。
int tryAcquireShared(int arg)
共享式获取同步状态,获取成功则返回值>=0
boolean tryReleaseShared(int arg)
共享式释放同步状态,成功返回true,失败返回false。
boolean isHeldExclusively()
判断同步器是否在独占模式下被占用,一般用来表示同步器是否被当前线程占用
  同步器提供的操作状态的方法:
方法
描述
int getState()
获取当前同步状态
void setState(int newState)
设置当前同步状态
boolean compareAndSetState(int expect, int update)
使用CAS设置当前状态,保证状态更新的原子性。只有当state为expect时,才能设置成功。expect相当于乐观锁的version
  同步器提供的模板方法:
方法
描述
void acquire(int arg)
独占式获取同步状态,该方法会调用子类重写的tryAcquire(int arg),如果tryAcquire返回true则该方法直接返回,否则先将当前线程加入同步队列的尾部,然后阻塞当前线程
void acquireInterruptibly(int arg)
和acquire类似,只是当线程获取同步状态失败被阻塞后,可以响应中断,收到中断后将会取消获取同步状态
boolean tryAcquireNanos(int arg, long nanosTimeout)
在acquireInterruptibly的基础上加了超时限制,如果在超时时间内获取到同步状态返回true,否则返回false
boolean release(int arg)
独占式释放同步状态,该方法会在释放同步状态后将第一个节点(对应刚刚释放同步状态的线程)的后继节点对应的线程唤醒。
void acquireShared(int arg)
共享式获取同步状态,该方法会调用子类重写的tryAcquireShared(int arg),如果tryAcquireShared返回true则该方法直接返回,否则先将当前线程加入同步队列的尾部,然后阻塞当前线程
void acquireSharedInterruptibly(int arg)
和acquireShared类似,只是当线程获取同步状态失败被阻塞后,可以响应中断,收到中断后将会取消获取同步状态
boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
在acquireSharedInterruptibly的基础上加了超时限制,如果在超时时间内获取到同步状态返回true,否则返回false
boolean releaseShared(int arg)
共享式的释放同步状态
Collection<Thread> getQueuedThreads()
获取等待在同步队列上的线程集合
   我们依据AQS来实现一个排它锁并进行测试:

排它锁代码:
public class Mutex implements Lock {
private final Sync sync = new Sync();
//静态内部类,自定义同步器,锁的功能通过它来实现
private static class Sync extends AbstractQueuedSynchronizer{
//是否处于占用状态
protected boolean isHeldExclusively(){
return getState() == 1;
}
//状态为0的时候,获取锁的方法
public boolean tryAcquire(int acquires) {
if(compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//释放锁,将状态设置为0
protected boolean tryRelease(int arg) {
if(getState()==0){
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);//移除占用线程的引用
setState(0);
return true;
}
Condition newCondition(){return new ConditionObject();}
}
//---以下是锁的具体实现,都是通过sync实现的---------
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(timeout));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
  我们建多个线程,对int值count进行累加。测试代码:
public class ThreadTest {
private int count;
private Mutex mutex = new Mutex();
@Test
public void addTest1(){
MyThread1 mt1 = new MyThread1("111");
MyThread1 mt2 = new MyThread1("222");
mt1.start();
mt2.start();
try {
mt1.join();
mt2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("addTest1 count = " + count);
}
@Test
public void addTest2(){
MyThread2 mt1 = new MyThread2();
MyThread2 mt2 = new MyThread2();
mt1.start();
mt2.start();
try {
mt1.join();
mt2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("addTest2 count = " + count);
} class MyThread1 extends Thread{
private String name;
public MyThread1(String name){ this.name=name;}
@Override
public void run() {
for(int i=0;i<1000;i++){
count = count + 1;
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
for(int i=0;i<1000;i++){
mutex.lock();
try {
count = count + 1;
}catch (Exception e){
System.out.println("异常啦 ~ ~ " +e.getMessage());
}finally {
mutex.unlock();
}
}
}
}
}
测试结果:
addTest1结果不确定,每次执行结果均小于2000,addTest2执行结果每次都是2000。显然,这是由于MyThread1中多线程同时执行,而累加过程又非原子操作,造成了内存覆盖导致。
 

队列同步器AbstractQueuedSynchronizer的更多相关文章

  1. Java 中队列同步器 AQS(AbstractQueuedSynchronizer)实现原理

    前言 在 Java 中通过锁来控制多个线程对共享资源的访问,使用 Java 编程语言开发的朋友都知道,可以通过 synchronized 关键字来实现锁的功能,它可以隐式的获取锁,也就是说我们使用该关 ...

  2. java并发编程的艺术——第五章总结(Lock锁与队列同步器)

    Lock锁 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同时访问共享资源(但有些锁可以允许多个线程访问共享资源,如读写锁). 在Lock接口出现前,java使用synchr ...

  3. Java 显示锁 之 队列同步器AQS(六)

    1.简述 锁时用来控制多个线程访问共享资源的方式,一般情况下,一个锁能够防止多个线程同时访问共享资源.但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁. 在Java 5.0之前,在协调对共享对 ...

  4. AbstractQueuedSynchronizer 队列同步器源码分析

    AbstractQueuedSynchronizer 队列同步器(AQS) 队列同步器 (AQS), 是用来构建锁或其他同步组件的基础框架,它通过使用 int 变量表示同步状态,通过内置的 FIFO ...

  5. Java中的队列同步器AQS

    一.AQS概念 1.队列同步器是用来构建锁或者其他同步组件的基础框架,使用一个int型变量代表同步状态,通过内置的队列来完成线程的排队工作. 2.下面是JDK8文档中对于AQS的部分介绍 public ...

  6. [Java并发] AQS抽象队列同步器源码解析--锁获取过程

    要深入了解java并发知识,AbstractQueuedSynchronizer(AQS)是必须要拿出来深入学习的,AQS可以说是贯穿了整个JUC并发包,例如ReentrantLock,CountDo ...

  7. AQS(抽象队列同步器)

    AQS(全称为AbstractQueuedSynchronizer),即抽象队列同步器,它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列. state的访问方 ...

  8. AQS(队列同步器)

    目录导引: 一.简介 二.源码解析(JDK8) 三.运用示例 一.简介 1.volatile volatile修饰的共享变量可以保证可见性和有序性(禁止指令重排序). 2.CAS: CAS的原理很简单 ...

  9. java高并发核心类 AQS(Abstract Queued Synchronizer)抽象队列同步器

    什么是AQS? 全称: Abstract Queued Synchronizer: 抽象队列同步器 是 java.util.concurrent.locks包下的一个抽象类 其编写者: Doug Le ...

随机推荐

  1. window7 下配置python2.7+tornado3.3开发环境

    玩python的人大都在linux下进行开发,由于长期习惯在windows下开发代码,今天蛋疼尝试在window7下配置python2.7+tornado3.3开发环境,必然的中间遇到各种报错,但是最 ...

  2. 【转】ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解

    原文地址:http://blog.csdn.net/a396901990/article/details/36475213 简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量—— ...

  3. .netcore部署到IIS上出现HTTP Error 502.5 - Process Failure问题解决

    首先网上是有很多解决方案,但是对我这个错误完全没用 如果你们没有环境首先得预装环境如下 1.首先在bing.com下搜索asp.net core download, 然后打开搜索到的信息.NET Do ...

  4. WinForm中ListView的使用

    每一行是一个ListViewItem对象,每一项是一个ListViewSubItem对象 样式 整行选择:this.lvDataSourceSearchHistory.FullRowSelect = ...

  5. 《C#多线程编程实战》2.6 ManualResetEventSlim

    这个比较好理解的. 正如书上所言,如同一直在打开的大门的屋子,谁要进去,谁就自己的关门,出来的时候在开开. 常用的方法 有三个: Set()  //设置为有信号,也就是让等待的线程不用继续等待,唤醒等 ...

  6. JavaScript定位导航滚动2

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  7. HDU6301-2018ACM暑假多校联合训练1004-Distinct Values

    题意是一个长度为n的序列,给你m组区间(l,r),在这个区间里不能填入重复的数字,同时使整个序列字典序最小 同学用的优先队列,标程里使用的是贪心同时使用set维护答案序列 贪心是先采用pre数组来确定 ...

  8. 帝国cms刷新内容页提示.phome_ecms_news_data_' doesn't exist

    帝国cms后台刷新提示.phome_ecms_news_data_' doesn't exist解决方法: 刷新所有信息内容页面时提示“Table '*.phome_ecms_article_data ...

  9. SDUT OJ 数据结构实验之串一:KMP简单应用 && 浅谈对看毛片算法的理解

    数据结构实验之串一:KMP简单应用 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Descr ...

  10. SDUT OJ 数据结构实验之排序八:快速排序

    数据结构实验之排序八:快速排序 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Problem Description 给定N ...