- 浏览: 483697 次
- 性别:
- 来自: 大连
文章分类
最新评论
-
龘龘龘:
TrueBrian 写道有个问题,Sample 1中,为了控制 ...
What's New on Java 7 Phaser -
龘龘龘:
楼主总结的不错。
What's New on Java 7 Phaser -
TrueBrian:
有个问题,Sample 1中,为了控制线程的启动时机,博主实际 ...
What's New on Java 7 Phaser -
liguanqun811:
不知道楼主是否对zookeeper实现的分布式锁进行过性能测试 ...
Distributed Lock -
hobitton:
mysql的get lock有版本限制,否则get lock可 ...
Distributed Lock
Inside AbstractQueuedSynchronizer (1)
Inside AbstractQueuedSynchronizer (2)
Inside AbstractQueuedSynchronizer (3)
Inside AbstractQueuedSynchronizer (4)
3 AbstractQueuedSynchronizer
3.1 Inheritance
AbstractQueuedSynchronizer继承自AbstractOwnableSynchronizer。AbstractOwnableSynchronizer继承自Object并且实现了Serializable接口,它只有一个成员变量private transient Thread exclusiveOwnerThread以及对应的getter/setter方法。该成员变量用于保存当前拥有排他访问权的线程。需要注意的是,该成员变量没有用volatile关键字修饰。
3.2 State
AbstractQueuedSynchronizer用一个int(private volatile int state)来保存同步状态,以及对应的getter/setter/compareAndSetState方法。Java6新增了一个AbstractQueuedLongSynchronizer,它用一个long来保存同步状态,貌似目前没有被java.util.concurrent中的其它synchronizer所使用。对于ReentrantLock,state为0则意味着锁没有被任何线程持有;否则state保存了持有锁的线程的重入次数。
3.3 WaitQueue
WaitQueue是AbstractQueuedSynchronizer的核心,它用于保存被阻塞的线程。它的实现是"CLH" (Craig, Landin, and Hagersten) lock queue的一个变种。
标准的CLH lock queue通常被用来实现spin lock,它通过TheadLocal变量pred引用队列中的前一个节点(Node本身没有指向前后节点的引用),以下是标准的CLH lock queue的一个参考实现:
public class ClhSpinLock { private final ThreadLocal<Node> pred; private final ThreadLocal<Node> node; private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node()); public ClhSpinLock() { this.node = new ThreadLocal<Node>() { protected Node initialValue() { return new Node(); } }; this.pred = new ThreadLocal<Node>() { protected Node initialValue() { return null; } }; } public void lock() { final Node node = this.node.get(); node.locked = true; Node pred = this.tail.getAndSet(node); this.pred.set(pred); while (pred.locked) {} } public void unlock() { final Node node = this.node.get(); node.locked = false; this.node.set(this.pred.get()); } private static class Node { private volatile boolean locked; } }
其逻辑并不复杂:对于lock操作,只需要通过一个CAS操作即可将当前线程对应的节点加入到队列中,并且同时获得了predecessor节点的引用,然后就是等待predecessor释放锁;对于unlock操作,只需要将当前线程对应节点的locked成员变量设置为false。unlock方法中的this.node.set(this.pred.get())主要目的是重用predecessor上的Node对象,这是对GC友好的一个优化。如果不考虑这个优化,那么this.node.set(new Node())也是可以的。跟那些TAS(test and set) spin lock和TTAS(test test and set) spin lock相比,CLH spin lock主要是解决了cache-coherence traffic的问题:每个线程在busy loop的时候,并没有竞争同一个状态,而是只判断其对应predecessor的锁定状态。如果你担心false sharing问题,那么可以考虑将锁定状态padding到cache line的长度。此外,CLH spin lock通过FIFO的队列保证了锁竞争的公平性。
AbstractQueuedSynchronizer的静态内部类Node维护了一个FIFO的等待队列。跟CLH不同的是,Node中包含了指向predecessor和sucessor的引用。predecessor引用的作用是为了支持锁等待超时(timeout)和锁等待回退(cancellation)的功能。sucessor的作用是为了支持线程阻塞:在Inside AbstractQueuedSynchronizer (1) 中提到过,AbstractQueuedSynchronizer通过LockSupport实现了线程的block/unblock,因此需要通过successor引用找到后续的线程并将其唤醒(CLH spin lock因为是spin,所以不需要显式地唤醒)。此外,Node中还包括一个volatile int waitStatus成员变量用于控制线程的阻塞/唤醒,以及避免不必要的调用LockSupport的park/unpark方法。需要注意的是,虽然AbstractQueuedSynchronizer在绝大多数情况下是通过LockSupport进行线程的阻塞/唤醒,但是在特定情况下也会使用spin lock,static final long spinForTimeoutThreshold = 1000L这个静态变量设定了使用spin lock的一个阈值。
对WaitQueue进行enqueue操作相关的代码如下:
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; } private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
addWaiter方法中的那个if分支,其实是一种优化,如果失败那么会调用enq方法进行enqueue。需要注意的是,以上代码中对next引用的设定是在enqueue成功之后进行的。这样做虽然没有并发问题,但是在判断一个node是否有sucessor时,不能仅仅通过next == null来判断,因为enqueue和设置next引用这两个步骤不是一个原子操作。
对WaitQueue进行dequeue操作相关的代码如下:
private void setHead(Node node) { head = node; node.thread = null; node.prev = null; }
setHead方法没有用到锁,也没有使用CAS,这样没有并发问题?没有,因为这个方法只会被持有锁的线程所调用,此时只需要将head指向持有锁的线程对应的node即可。
评论
但是有一点不太理解,请问ClhSpinLock中的ThreadLocal<Node> pred有什么作用呢?
在lock时可以通过tail来获取前任节点,然后判断其状态来决定是否自选等待,那为什么还需要一个本地线程的pred来储存前任节点呢?在unlock时,只需要将当前节点的locked状态修改为false就可以释放后继结点,this.node.set(this.pred.get()); 貌似没有实质性的作用呀?
和 lixieinstein 的想法一致,感觉用AtomicReference<Node> pred, 就行了,连ThreadLocal<Node> node都没必要吧? 这样锁的实现应该不可以重入。
谢谢回复,:D ,不可以重入,如果要重入就要判断当前线程是否持锁线程,然后还要对加锁次数和释放锁次数进行操作。这个例子让我对CLH队列有了很好的理解,呵呵
但是有一点不太理解,请问ClhSpinLock中的ThreadLocal<Node> pred有什么作用呢?
在lock时可以通过tail来获取前任节点,然后判断其状态来决定是否自选等待,那为什么还需要一个本地线程的pred来储存前任节点呢?在unlock时,只需要将当前节点的locked状态修改为false就可以释放后继结点,this.node.set(this.pred.get()); 貌似没有实质性的作用呀?
和 lixieinstein 的想法一致,感觉用AtomicReference<Node> pred, 就行了,连ThreadLocal<Node> node都没必要吧? 这样锁的实现应该不可以重入。
但是有一点不太理解,请问ClhSpinLock中的ThreadLocal<Node> pred有什么作用呢?
在lock时可以通过tail来获取前任节点,然后判断其状态来决定是否自选等待,那为什么还需要一个本地线程的pred来储存前任节点呢?在unlock时,只需要将当前节点的locked状态修改为false就可以释放后继结点,this.node.set(this.pred.get()); 貌似没有实质性的作用呀?
发表评论
-
Understanding the Hash Array Mapped Trie
2012-03-30 10:36 0mark -
Atomic Bit Operation in Linux Kernel
2012-02-08 00:27 2099Linux Kernel支持atomic bit operat ... -
A Hierarchical CLH Queue Lock
2012-01-14 19:01 2150A Hierarchical CLH Queue Lock ( ... -
Inside AbstractQueuedSynchronizer (4)
2012-01-08 17:06 3520Inside AbstractQueuedSynchroniz ... -
Inside AbstractQueuedSynchronizer (3)
2012-01-07 23:37 4729Inside AbstractQueuedSynchroniz ... -
Inside AbstractQueuedSynchronizer (1)
2012-01-06 11:04 7948Inside AbstractQueuedSynchroniz ... -
Code Optimization
2011-10-14 00:11 1607当前开发人员在进行编码的时候,可能很少关注纯粹代码级别的优化了 ... -
Distributed Lock
2011-08-02 22:02 92101 Overview 在分布式系统中,通常会 ... -
What's New on Java 7 Phaser
2011-07-29 10:15 82721 Overview Java 7的并 ... -
Sequantial Lock in Java
2011-06-07 17:00 22141 Overview Linux内核中常见的同步机 ... -
Feature or issue?
2011-04-26 22:23 121以下代码中,为何CglibTest.intercept ... -
Bloom Filter
2010-10-19 00:41 50731 Overview Bloom filt ... -
Inside java.lang.Enum
2010-08-04 15:40 64761 Introduction to enum J ... -
Open Addressing
2010-07-07 17:59 34581 Overview Open addressi ... -
JLine
2010-06-17 09:11 11008Overview JLine 是一个用来处理控 ... -
ID Generator
2010-06-14 14:45 1676关于ID Generator,想 ... -
inotify-java
2009-07-22 22:58 82971 Overview 最近公 ... -
Perf4J
2009-06-11 23:13 84871 Overview Perf4j是一个用于计算 ... -
Progress Estimator
2009-02-22 19:37 1534Jakarta Commons Cookbook这本书 ... -
jManage
2008-12-22 00:40 39571 Overview 由于项目需要, 笔者开发了一个 ...
相关推荐
Inside Ole2的源码
Inside OLE 2nd Edition
2. 在调用P/Invoke之前,必须理解非托管代码的API接口,包括参数类型、返回值和错误处理机制。 3. 考虑到.NET和C/C++之间的类型系统差异,如指针和引用的处理,确保在转换过程中正确处理这些差异。 4. 注意平台兼容...
Inside Bluetooth Low Energy (Mobile Communications) By 作者: Naresh Gupta ISBN-10 书号: 1630810894 ISBN-13 书号: 9781630810894 Edition 版本: 2nd ed. Release 出版日期: 2016-06-30 pages 页数 (458)
2. 组件对象模型(COM):OLE 2.0的核心是COM,它定义了一种跨进程的接口标准,使得不同的软件组件可以无须了解彼此的内部实现就能互相通信。COM规定了对象的创建、查询接口、方法调用和生命周期管理等规范。 3. ...
Allegro自带的ODB++inside工具下载,ODB++inside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_...
《Inside OLE2》这本书是深入理解OLE技术的权威指南,尤其对于那些在国内难以找到相关中文资料的读者来说,它是一本极其宝贵的资源。OLE,全称Object Linking and Embedding,是微软在1990年代推出的一种技术,旨在...
Allegro自带的ODB++inside工具下载,ODB++inside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_...
Allegro自带的ODB++inside工具下载,ODB++inside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_...
Allegro自带的ODB++inside工具下载,ODB++inside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_...
【Inside OLE 2nd Edition】是一本深入探讨OLE 2技术的专业书籍,该书并非官方中文翻译,而是供个人学习和参考的资料。OLE 2是一个基于复合文档的组件对象模型,它不仅解决了复合文档的问题,也作为一个可扩展的服务...
2. **对象表示**:C++中的对象不仅包含数据成员,还包括隐含的this指针和可能的虚函数表。理解这些细节对于优化代码和调试至关重要。书中详细阐述了对象在内存中的布局,包括非静态成员变量和静态成员变量的存储位置...
《Inside Ole 2nd》是Windows编程领域的一部经典著作,深入探讨了组件对象模型(Component Object Model,简称COM)的内部机制和技术细节。这本书在第二版中详细介绍了OLE技术的发展,以及如何利用COM来构建分布式...
Allegro自带的ODB++inside工具下载,DBinside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_Inside_...
2. **Calling Convention**:如stdcall、cdecl等,决定参数如何被压入堆栈和谁负责清理堆栈。 3. **Marshaling**:处理不同类型之间的转换,如.NET的托管类型到非托管类型的映射。 4. **Struct Marshaling**:对于...
Microsoft Excel 2010 Inside Out 英文无水印原版pdf pdf所有页面使用FoxitReader、PDF-XChangeViewer、SumatraPDF和Firefox测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细...
2. **完整的制造信息**:ODB++包含所有必要的制造细节,如丝印、焊盘、孔径、层堆栈等,确保了制造过程的精确性。 3. **灵活的约束管理**:通过ODB++,设计师可以定义并传达复杂的制造约束,如阻抗控制、平面分割等...
文档标题为《Inside游戏效果文档》,描述了PlayDead公司在制作游戏《Inside》时所采用的渲染技术。文档内容涉及了游戏中的雾效、HDR泛光(bloom)以及其他与光照和渲染相关的高级技术。这些技术对于创造游戏的独特...
2. **字节码执行**: JVM通过解释器将字节码转换为机器指令执行。此外,现代JVM如HotSpot还引入了即时编译(JIT)技术,将频繁执行的代码编译为本地机器代码,以提高性能。 3. **内存管理**: JVM内存分为堆内存...