关闭

JUC之AQS

443人阅读 评论(0) 收藏 举报
分类:
AQS是同步框架,它进行两个方面的工作:资源的管理和资源申请者的管理。对应由两部分组成:一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。state的访问方式有三种:
	getState()
	setState()
	compareAndSetState()


AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

实现自定义同步器时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。

自定义同步器实现时主要实现以下几种方法:
	isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
	tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
	tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
	tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
	tryReleaseShared(int):共享方式。尝试释放资源,成功则返回true,失败则返回false。


以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

  再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock。
理解了这些,可以很容易的自定义同步器类,如下例子:

class Mutex implements Lock , Serializable{
    private static class Syn extends AbstractQueuedSynchronizer{
        @Override
        protected boolean isHeldExclusively() {
            return getState()==1;
        }
        @Override
        protected boolean tryAcquire(int arg) {
            assert arg==1;
            if(compareAndSetState(0,1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        @Override
        protected boolean tryRelease(int arg) {
            assert arg==1;
            if(getState()==0)
                throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }
    private Syn syn=new Syn();
    public void lock() {
        syn.acquire(1);
    }
    public void unlock() {
        syn.release(1);
    }
    public void lockInterruptibly() throws InterruptedException{
        syn.acquireInterruptibly(1);
    }
    public boolean tryLock(){
        return syn.tryAcquire(1);
    }
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException{
        return syn.tryAcquireNanos(1,unit.toNanos(time));
    }
    public Condition newCondition(){
        return null;
    }
}


上面说,AQS对资源申请者的管理已经在顶层实现好了,自定义同步器时仅仅需要重写几种特定的方法,不需要关心队列的实现细节。但这里还是简要的了解下。
先看acquire()方法:acquire()方法是独占模式获取资源的顶级入口。代码如下:

 public final void acquire(int arg) {
     if (!tryAcquire(arg) &&
         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
         selfInterrupt();
 }


流程如下:
1. 调用自定义同步器的tryAcquire()尝试直接去获取资源,如果成功则直接返回;
2. 没成功,则addWaiter()将该线程加入等待队列的尾部,并标记为独占模式;
3. acquireQueued()使线程在等待队列中休息,有机会时(轮到自己,会被unpark())会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
4. 如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。

再看release()方法,release()方法是独占模式下释放资源的顶级入口。代码如下:

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;//找到头结点
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);//唤醒等待队列里的下一个线程
        return true;
    }
    return false;
}

至此,关于AQS的简单介绍就结束了。AQS是许多常用类的基础,比如ReentrantLock,Semaphore,ReentrantReadWriteLock,CountDownLatch等等,都是基于AQS构建的。感兴趣的话可以去看他们的实现。
0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

JUC之AQS框架

AQS是一个框架,一个提供锁或同步器依赖于`FIFO等待队列`所必要的“基础设施”的框架。**Dong Lea**之所以写个抽象类的目的是为了简化我们实现同步器的工作。 提供一个基于FIFO等待队列...
  • zteny
  • zteny
  • 2017-02-08 00:38
  • 378

JUC(Lock)和Monitor Object(Synchronized)机制区别是什么

这是一道面试题 synchronized和lock的用法区别 synchronized和lock性能区别 Refer 一、synchronized和lock的用法区别 synchronized:在...
  • m_xiaoer
  • m_xiaoer
  • 2017-06-18 17:41
  • 646

JUC 基础内容概述

Concurrent Programming in Java 的作者 Doug Lea 编写了一个极其优秀的、免费的并发实用程序包,它包括并发应用程序的锁、互斥、队列、线程池、轻量级任务、有效的并发集...
  • kjfcpua
  • kjfcpua
  • 2013-08-23 12:25
  • 4784

聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一)

AQS是AbstractQueuedSynchronizer的缩写,AQS是Java并包里大部分同步器的基础构件,利用AQS可以很方便的创建锁和同步器。它封装了一个状态,提供了一系列的获取和释放操作,...
  • ITer_ZC
  • ITer_ZC
  • 2014-11-06 14:16
  • 4187

JUC线程池--线程池架构

线程池的架构图如下: Executor 它是”执行者”接口,它是来执行任务的。准确的说,Executor提供了execute()接口来执行已提交的 Runnable 任务的对象。Executor存在...
  • Hey_WonderfulWorld
  • Hey_WonderfulWorld
  • 2017-04-27 13:52
  • 363

剖析基于并发AQS的共享锁的实现(基于信号量Semaphore)

【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/76167357 出自【...
  • javazejian
  • javazejian
  • 2017-07-31 09:33
  • 5698

【死磕Java并发】-----J.U.C之AQS:AQS简介

Java的内置锁一直都是备受争议的,在JDK 1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6后,进行大量的锁优化策略(【死磕Java并发】—–深入分析synchr...
  • chenssy
  • chenssy
  • 2017-03-05 22:26
  • 4793

Java并发框架——AQS中断的支持

线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止。在java中要让线程安全、快速、可靠...
  • wangyangzhizhou
  • wangyangzhizhou
  • 2014-11-23 22:49
  • 2292

学习笔记 07 --- JUC集合

学习笔记 07 --- JUC集合 在讲JUC集合之前我们先总结一下Java的集合框架,主要包括Collection集合和Map类,Collection集合又可以划分为LIst和Set。 1....
  • tianya3530
  • tianya3530
  • 2017-01-20 10:35
  • 1389

Java并发编程-Lock和condition的原理及AQS的运用

AQS的全称为(AbstractQueuedSynchronizer),这个类也是在java.util.concurrent.locks下面。这个类似乎很不容易看懂,因为它仅仅是提供了一系列公共的方法...
  • chenchaofuck1
  • chenchaofuck1
  • 2016-08-16 19:33
  • 2523
    个人资料
    • 访问:142611次
    • 积分:1691
    • 等级:
    • 排名:千里之外
    • 原创:139篇
    • 转载:10篇
    • 译文:0篇
    • 评论:36条
    最新评论