`
ChristmasLin
  • 浏览: 42091 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Concurrent Synchronizer Framework(3)

阅读更多

 

这章来自3.3Queues》。

 

         这个framework的核心是排队阻塞线程。这里的排队限制为先进先出,因此,framework不支持基于优先级的排队。

         对于同步队列使用非阻塞队列,从而不用构造一些low-level的锁这一个选择,几乎是没有争论的。有两个选择,

CLH LockMCS Lock。原始的CLH Lock仅仅使用自旋锁,但是相对于MSC Lock,它更容易处理canceltimeout,所以选择了CLH Lock。最后设计出来的变种CLH Lock和原始的CLH Lock有较大的差别,需要描述一下。

 

         一个CLH Queue看起来不太像一个queue,因为它的出队和入队操作像是一个锁(原文:because its enqueuing and dequeuing operations are intimately tied to its uses as a lock)。它是一个实用两个原子更新字段的linked queue。分别是headtail,在初始化时指向同一个node

 

         一个新的node入队时是一个原子操作:

         do { pred = tail;} while(!tail.compareAndSet(pred, node));

 

         每个noderelease 状态保存在它的前驱node中,所以它的自旋锁的自旋操作:

         while (pred.status != RELEASED) ; // spin

        

出队操作在自旋后,要把head设置为node

head = node;

 

CLH Lock的优点:出队和入队都非常快;lock-free;无障碍(obstruction free,即使是在竞争情况下,总有一个线程赢得竞争);快速检测是否有其他线程在等待(比较head是否等于tail);release 状态是分散的,避免了内存竞争。

 

在原始版本的CLH Locknodes是没有被链起来的(见《线程锁系列(1):CLH Lock》)。但是如果每个node维护一个指向前驱的指针,CLH Lock可以处理timeout和一些cancel操作:如果一个node的前驱被cancle,这个node可以上滑使用前驱的状态字段。

 

为了使CLH Queues适合blocking synchronizers,一个额外的主要改动是提供node快速定位它的后继。在自旋锁里,node只需要改变自身状态,后继会在下一个自旋发现状态的改变。所以链接到后继是不必要的。但是在blocking synchronizers里,node需要明确的唤醒(unpark))它的后继。

 

一个AbstractQueuedSynchronizer queue node包含指向后继和前驱的指针。但是对于双向链表,没有使用compareAndSet,合适的lock-free,原子插入的算法。对指向后继的指针赋值不是插入操作的原子的一部分。它只是被简单的赋值:pred.next = node。插入后,会反映在所有的用法上。指向后继的指针只是一个优化的路径,如果一个后继好像不存在(可能被cancel),会定位到列表的tail,通过前驱指针,向上遍历,精确的检测是否有后继。

 

第二个变动是在每个node里使用一个状态字段去控制阻塞,而不是自旋。在framework里,一个排队的线程调用acquire,只有在通过了子类实现的tryAcquire才能返回。一个单独的“release”位是不能满足的。控制方法保证了只有在队里头部的活动线程才允许调用tryAcquire;在某些情况,线程可能acquire失败,重新block。这个保证不需要额外的状态,只需要检测node的前驱是否是head。和自旋锁不一样,通过复制减少了读head的内存冲突(And unlike the case of spinlocks, there is not enough memory contention reading head to warrant replication)。但是,cancle的状态需要在状态位描述。

 

Queue nodes 状态字段也会被用来避免不必要的parkunpark。当调用方法相当的快,它们有可能可以避免在jvmos中切换(parkunpark需要在jvmos中切换)。一个线程在调用park前,会把状态位设为“single me”,在park前再一次的检测状态位。一个释放lock的线程会清除这个状态位。这避免了不必要的block,通常来说,引入的额外开销也是值得的,特别是锁类,等待下一个获得线程的锁浪费了时间,加重竞争。这也避免了一个释放线程需要决定它的后继,除非后继状态位被设成“single me”。这可以消除后继指针为null的遍历开销,但是如果是cancel,开销仍然无法避免。

 

framework里的CLH Locks和其他语言实现的CLH Locks的最大不同可能是nodes的回收利用依赖于GC。它减少了复杂性和开销。但是即使依赖于GC,当指针被确定不需要再使用,仍然需要把它们置null,这通常发生在出队。否则仍然是可达的,导致不能被回收。

 

另外还有一些微小的改动,包括head使用的假节点延迟初始化。

 

忽略这些细节,一个基本的acquire实现(独占的,不可中断的,没有超时的)大体如下:

 

if (!tryAcquire(arg)) {
	node = create and enqueue new node;
	pred = node's effective predecessor;
	while (pred is not head node || !tryAcquire(arg)) {
		if (pred's signal bit is set)
			park();
		else
			compareAndSet pred's signal bit to true;
			pred = node's effective predecessor;
	}
		head = node;
}
 

Release操作如下:

 

if (tryRelease(arg) && head node's signal bit is set) {
	compareAndSet head's signal bit to false;
	unpark head's successor, if one exists
}
 

对于acquire,循环的次数依赖于tryAcquire,如果不考虑cancel和分摊parkos的线程调度,acquirerelease都是O1)操作。

 

为了支持cancel,在acquire循环中,需要检查超时和中断。一个由超时或中断引起的被取消的线程需要设置它的状态和unpark它的后继,所以它可能重置指针。随着cancel,决定前驱后继和重置状态,可能会包含On)的遍历(nQueue的长度)。因为一个已经被cancle的线程永远不可能再次阻塞,链接和状态需要被迅速的重建。

分享到:
评论

相关推荐

    The java.util.concurrent Synchronizer Framework

    ### Java.util.concurrent同步器框架详解 #### 概述 Java平台在J2SE 1.5版本中引入了`java.util.concurrent`包,这是一系列中等层次的并发支持类集合,通过Java社区过程(Java Community Process, JCP)的Java规范...

    The java. util. concurrent synchronizer framework.pdf

    AQS(AbstractQueuedSynchronizer)是Java.util.concurrent包中同步器的基础框架,它的核心设计思想与实现方法在Doug Lea先生的这篇论文中有详细的介绍。论文详细阐述了AQS框架的原理、设计、实现、应用以及性能等...

    The java.util.concurrent synchronizer framework.pdf

    文档标题“java.util.concurrent同步器框架”和描述“Doug Lea的java.util.concurrent同步器框架”表明本文将探讨由Doug Lea所撰写的关于Java并发编程中同步器框架的内容。文档中提到了AbstractQueuedSynchronizer类...

    Light, concurrent RPC framework for PHP & C.zip

    "Light, concurrent RPC framework for PHP & C" 是一个专为PHP和C语言设计的轻量级、并发RPC框架,旨在提高性能和可扩展性。 1. 轻量级:这个框架可能专注于减少资源消耗和提高启动速度,这通常是通过精简设计...

    concurrent 摘自guava concurrent

    这个工程是为了学习guava concurrent中的AbstractFuture而建立的,里面有可以运行的例子,再配合我的博客:https://blog.csdn.net/o1101574955/article/details/82889851,可以看明白guava concurrent的基本设计思路...

    CoDeK - Concurrent Development frameworK-开源

    CoDeK-Java并发开发frameworK是一个非常简单的开源学术Java库,旨在帮助开发Java多线程并发应用程序。

    backport-util-concurrent.jar

    backport-util-concurrent.jarbackport-util-concurrent.jarbackport-util-concurrent.jar

    concurrent-1.3.4.jar

    concurrent-1.3.4.jar

    backport-util-concurrent(2.2 /3.1)

    3. **ThreadPoolExecutor的前身:ThreadPool** - 在Java 5之前,Java标准库并没有提供如ThreadPoolExecutor这样的高级线程池实现。backport-util-concurrent提供了ThreadPool,它是Java 5 ThreadPoolExecutor的一个...

    JDK concurrent

    标题 "JDK concurrent" 指的是Java开发工具包(JDK)中的并发编程相关知识。并发编程是在多线程环境中同时执行多个任务的技术,它在现代计算机系统中至关重要,尤其是在多核处理器和高并发应用中。Java JDK提供了一...

    LoadRunner函数中文翻译系列之3--Concurrent Group

    3. **性能优化**:通过分析并发组测试结果,识别瓶颈并优化系统性能。 ### 结论 LoadRunner中的并发组功能是进行性能测试的关键工具之一。通过合理地利用 `web_concurrent_start()` 和 `web_concurrent_end()` ...

    concurrent-1.3.2.jar

    concurrent-1.3.2.jar concurrent-1.3.2.jar

    Concurrent.Thread.js javascript多线程

    Concurrent.Thread.js 一个用来让javascript也进行多线程开发的包,感兴趣的快来下吧。

    concurrent线程池的实现技术分析

    3. **有限链接队列算法**:与同步通道类似,但限制了队列的容量。队列满时,若线程数未达最大,仍会创建新线程。在阻塞情况下,等待线程池有空闲线程后再放入任务。 **concurrent包的线程池实现** `concurrent`...

    Concurrent.Thread.js

    《并发编程:JavaScript中的Concurrent.Thread.js》 在IT领域,多线程编程是一种常见的优化技术,用于提高程序的执行效率。特别是在JavaScript这样的单线程环境中,由于其异步执行模型,多线程处理显得尤为重要。...

    concurrent_.jar

    concurrent.jar web开发工具包

    Simplx-a C++ framework distributed and concurrent multicore software

    Simplx is a C++ development framework for building reliable cache-friendly distributed and concurrent multicore software. Simplx was developped by Tredzone SAS. We provide software technology ...

    Python库 | concurrent_log_handler-0.9.4-py2.py3-none-any.whl

    资源分类:Python库 所属语言:Python 使用前提:需要解压 资源全名:concurrent_log_handler-0.9.4-py2.py3-none-any.whl 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

Global site tag (gtag.js) - Google Analytics