Java并发框架——同步状态的管理

整个AQS框架核心功能都是围绕着其32位整型属性state进行,一般可以说它表示锁的数量,对同步状态的控制可以实现不同的同步工具,例如闭锁、信号量、栅栏等等。为了保证可见性此变量被声明为volatile,保证每次的原子更新都将及时反映到每条线程上。而对于同步状态的管理可以大体分为两块,一是独占模式的管理,另外是共享模式的管理。通过对这两种模式的灵活变换可以实现多种不同的同步器,如下图,对state的控制可以看成一个管道,管道的大小决定了同时通过的线程,独占模式好比宽度只容许一个线程通过的管道,在这种模式下线程只能逐一通过管道,任意时刻管内只能存在一条线程,这便形成了互斥效果。而共享模式就是管道宽度大于1的管道,可以同时让n条管道通过,吞吐量增加但可能存在共享数据一致性问题。(注意:两种模式的讨论忽略了队列的管理逻辑,实际上CLH Node的引入是为了优化竞争带来的性能问题,不影响同步状态管理的探讨)
 
图2-5-9-5 独占模式与共享模式
如何通过state实现独占模式和共享模式?在此之前先了解AQS框架中相关的getState、setState、compareAndSetState三个操作state的基本方法,前两个方法是普通的获取设置方法,其必须保证不存在数据竞争的情况下使用,compareAndSetState方法则提供了CAS方式的硬件级别的原子更新。两种模式就是通过这些方法对state操作实现不同同步模式,下面给出最简单的实现。
 独占模式
public boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
return true;
}
return false;
}
protected boolean tryRelease(int releases) {
setState(0);
return true;
}
多条线程通过tryAcquire尝试把state变量改为1,由于CAS算法的保证,最终有且仅有一条线程成功修改state,修改成功的线程代表获取锁成功,将拥有往下执行的权利,进入管道。当执行完毕退出管道时执行tryRelease尝试把state变量改为0,让出管道,此处由于不存在线程竞争所以可直接使用setState,接着其他未通过的线程继续重复尝试。
 共享模式
public int tryAcquireShared(int interval) {
for (;;) {
int current = getState();
int newCount = current - 1;
if (newCount < 0 || compareAndSetState(current, newCount)) {
return newCount;
}
}
}
public boolean tryReleaseShared(int interval) {
for (;;) {
int current = getState();
int newCount = current + 1;
if (compareAndSetState(current, newCount)) {
return true;
}
}
}
与独占模式不同的是对state的管理及判断条件,独占模式state的值只能为0或1,而共享模式的state是可以被出事换成任意整数,一般初始值表示提供一个同时n条线程通过的管道宽度,这样一来,多条线程通过tryAcquireShared尝试将state的值减去1,成功修改state后就返回新值,只有当新值大于等于0才表示获取锁成功,拥有往下执行的权利,进入管道。在执行完毕时线程将调用tryReleaseShared尝试修改state值使之增加1,表示我已经执行完了并让出管道的通道供后面线程使用,需要说明的是与独占模式不同,由于可能存在多条线程并发释放锁,所以此处必须使用基于CAS算法的修改方法,修改成功后其他线程便可继续竞争锁。

ASQ框架提供了对同步状态state的基本操作,了解了两种模式对state操作开发者可能很自由地自定义自己的同步器。实际中AQS框架在提供state状态管理接口的同时也将维护等待队列的工作,两项工作被封装成一个模板,规定了工作流程,工作流程包括什么条件下加入等待队列、什么条件移除等待节点、如何操作等待队列、需不需要阻塞、支不支持中断等等,对外仅仅提供state状态操作接口供开发者自定义,而队列的维护工作已经绑定在模板中,无需你自己动手。

点击订购作者书籍《Tomcat内核设计剖析》

时间: 2024-12-31 02:33:35

Java并发框架——同步状态的管理的相关文章

Java并发框架——AQS阻塞队列管理(一)——自旋锁

我们知道一个线程在尝试获取锁失败后将被阻塞并加入等待队列中,它是一个怎样的队列?又是如何管理此队列?这节聊聊CHL Node FIFO队列. 在谈到CHL Node FIFO队列之前,我们先分析这种队列的几个要素.首先要了解的是自旋锁,所谓自旋锁即是某一线程去尝试获取某个锁时,如果该锁已经被其他线程占用的话,此线程将不断循环检查该锁是否被释放,而不是让此线程挂起或睡眠.它属于为了保证共享资源而提出的一种锁机制,与互斥锁类似,保证了公共资源在任意时刻最多只能由一条线程获取使用,不同的是互斥锁在获取

Java并发框架——AQS阻塞队列管理(三)——CLH锁改造

在CLH锁核心思想的影响下,Java并发包的基础框架AQS以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能.比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头结点和尾节点,他们分别指向队列的头和尾,尝试获取锁.入队列.释放锁等实现都与头尾节点相关,并且每个节点都引入前驱节点和后后续节点的引用:在等待机制上由原来的自旋改成阻塞唤醒.如图2-5-9-4,通过前驱后续节点的引用一节节连接起来形成一个链表队列,对于头尾节点的

Java并发框架——AQS阻塞队列管理(二)——自旋锁优化

看Craig, Landin, and Hagersten发明的CLH锁如何优化同步带来的花销,其核心思想是:通过一定手段将所有线程对某一共享变量轮询竞争转化为一个线程队列且队列中的线程各自轮询自己的本地变量.这个转化过程由两个要点,一是构建怎样的队列&如何构建队列,为了保证公平性,构建的将是一个FIFO队列,构建的时候主要通过移动尾部节点tail实现队列的排队,每个想获取锁的线程创建一个新节点并通过CAS原子操作将新节点赋予tail,然后让当前线程轮询前一节点的某个状态位,如图2-5-9-3,

Java并发框架——什么是AQS框架

什么是AQS框架 1995年sun公司发布了第一个java语言版本,可以说从jdk1.1到jdk1.4期间java的使用主要是在移动应用和中小型企业应用中,在此类领域中基本不用设计大型并发场景,当然也没有大型互联网公司使用java,因为担心它本身的性能.在互联网及服务器硬件迅猛的发展下,sun公司更加注重企业级应用方面,毫无疑问高并发是一个重要的主题,于是在J2SE5.0(jdk1.5)代号为老虎的版本中增加了更加强大的并发相关的操作包--java.util.concurrent.此后java在

Java并发框架——AQS之如何使用AQS构建同步器

AQS的设计思想是通过继承的方式提供一个模板让大家可以很容易根据不同场景实现一个富有个性化的同步器.同步器的核心是要管理一个共享状态,通过对状态的控制即可以实现不同的锁机制.AQS的设计必须考虑把复杂重复且容易出错的队列管理工作统一抽象出来管理,并且要统一控制好流程,而暴露给子类调用的方法主要就是操作共享状态的方法,以此提供对状态的原子性操作.一般子类的同步器中使用AQS提供的getState.setState.compareAndSetState三个方法,前两个为普通的get和set方法,要使

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

线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠地停下来并不是一件容易的事,java也没有提供任何可靠的方法终止线程的执行.回到第六小节,线程调度策略中有抢占式和协作式两个概念,与之类似的是中断机制也有协作式和抢占式. 历史上Java曾经使用stop()方法终止线程的运行,他们属于抢占式中断.但它引来了很多问题,早已被JDK弃用.调用stop()

Java并发框架——AQS超时机制

AQS框架提供的另外一个优秀机制是锁获取超时的支持,当大量线程对某一锁竞争时可能导致某些线程在很长一段时间都获取不了锁,在某些场景下可能希望如果线程在一段时间内不能成功获取锁就取消对该锁的等待以提高性能,这时就需要用到超时机制.在JDK1.5之前还没有juc工具,当时的并发控制职能通过JVM内置的synchronized关键词实现锁,但对一些特殊要求却力不从心,例如超时取消控制.JDK1.5开始引入juc工具完美解决了此问题,而这正得益于并发基础框架AQS提供了超时的支持.为了更精确地保证时间间

Java并发框架——AQS之原子性如何保证?

在研究AQS框架时,会发现这个类很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面,肯定要通过用C++编写的native本地方法来扩展实现.JDK提供了一个类来满足CAS的要求,sun.misc.Unsafe,从名字上可以大概知道它用于执行低级别.不安全的操作,AQS就是使用此类完成硬件级别的原子操作. Unsafe是一个很强大的类,它可以分配内存.释放内存.可以定位对象某字段的位置.可

Java并发框架——公平性

所谓公平性指所有线程对临界资源申请访问权限的成功率都一样,不会让某些线程拥有优先权.通过前面的CLH Node FIFO学习知道了等待队列是一个先进先出的队列,那么是否就可以说每条线程获取锁时就是公平的呢?关于公平性这里分拆成三个点分别阐述:① 准备入队列的节点,此情况讨论的是线程加入等待队列时产生的竞争是否公平,线程在尝试获取锁失败后将被加入等待队列,这时多个线程通过自旋将节点加入队列,所有线程在自旋过程中是无法保证其公平性的,可能后来的线程比早到的先进入队列,所以节点入队列不具公平性.② 等