看完书 java concurrency in practice 当然是想找点啥好玩的东东玩玩。 当看到了Doug Lee 的论文 << The java.util.concurrent Synchronizer Framework >> 大呼来的太晚喔, 前段时间看那个ReentrantLock 的代码真的是痛苦啊,不过现在也不晚不是。  呵呵, 上菜:这个框架的核心是一个AbstractQueuedSynchronizer 类 (下面简称AQS)  它基本上的思路是:

采用Template Method Pattern.  它实现了non-contended 的synchronization 算法;

继承 它的Subclass  一般不直接作为Synchronzier, 而是作为私有的实现 被用来delegate.  比如 他举了个例子:

class Mutex implements Lock, java.io.Serializable {

Java代码

// Our internal helper class

private static class Sync extends AbstractQueuedSynchronizer {

.....

}

// The sync object does all the hard work. We just forward to it.

private final Sync sync = new Sync();

public void lock()                { sync.acquire(1); }

public boolean tryLock()          { return sync.tryAcquire(1); }

public void unlock()              { sync.release(1); }

public Condition newCondition()   { return sync.newCondition(); }

public boolean isLocked()         { return sync.isHeldExclusively(); }

public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }

public void lockInterruptibly() throws InterruptedException {

sync.acquireInterruptibly(1);

}

public boolean tryLock(long timeout, TimeUnit unit)

throws InterruptedException {

return sync.tryAcquireNanos(1, unit.toNanos(timeout));

}

在这个 AQS 类中 提供了 两大类方法

acquire   其中还包括  非阻塞的 tryAcquire;  带 time out 的;可以通过interruption 来cancellality 的。

release   相对简单点, 因为在调用release 方法的时候基本上暗含了个意思当前的线程是获得了锁的。

在JUC 包中没有为各种Synchronizer 类提供统一的API 比如 Lock.lock, Semaphore.acquire, CountDownLatch.await  and  FutureTask.get 都是映射到这个AQS 的 acquire 方法。

要实现一个Synchronizer 的基本思路非常直接的

acquire 操作

采用CAS 操作去更新同步状态  如果不成功  {

enqueue 当前线程

block 当前线程

}

dequeue  当前线程  if it was queued      // 此时应该是当前线程被别的线程 release 来唤醒的。

release 操作 :

更新同步状态, 此时可以直接 set 而不通过 CAS。

假如 等待队列中还有线程  unblock  one or more

要支持这些操作 需要 三类基本的组件:

1,   Atomically managing synchronization state。   这个通过

private   volatile  int   state;

在处理acquire 的时候基本是通过用CAS 实现的  compareAndSetState 来

2,   Block /Unblock 线程 。 这个是通过 JUC 里面的一个封装类 LockSupport.park / unpark 来实现的。LockSupport 都deleget 到了 Unsafte 对应的方法

3, 维护 一个队列来存放 等待线程。 这个是通过一个 CLH queue 的变种。 它是一种linked queue 的通过 head 和tail 。

三个里面最复杂的就是这个 CLH queue 的维护了, queue 中节点被定义为  :

static final class Node {

Java代码

volatile int waitStatus;

volatile Node prev;

volatile Node next;

volatile Thread thread;

Node nextWaiter;

final boolean isShared() {

return nextWaiter == SHARED;

}

final Node predecessor() throws NullPointerException {

Node p = prev;

if (p == null)

throw new NullPointerException();

else

return p;

}

Node() {    // Used to establish initial head or SHARED marker

}

Node(Thread thread, Node mode) {     // Used by addWaiter

this.nextWaiter = mode;

this.thread = thread;

}

Node(Thread thread, int waitStatus) { // Used by Condition

this.waitStatus = waitStatus;

this.thread = thread;

}

AQS 中定义了

Java代码

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;

}

这个方法通常在 acquire 的时候如果 tryAcquire 失败就会将insert 一个 Node 到 tail 之后, 并且 node.prev 指向先前的 tail。 当然这个是在 compareAndSetTail(pred, node)  返回true 的时候比较简单, 否则就进入 enq(node) 方法 差不多也是这个逻辑insert 一个 Node 到 tail 之后。

在 1.6.0_25 版本里面AQS 就没有在 release 的时候 通过唤醒等待队列里面head指向的线程, 然后然后该线程是在 方法里面继续resume, 基本上会

if (p == head && tryAcquire(arg)) {

setHead(node);

p.next = null; // help GC

return interrupted;

}。

就是将head的下一个节点设为Head, 原来的head 呢 p.next = null,  p 不再引用任何对象, gc 就会发生作用。  这个基本上会的例外是啥呢, 新线程acquire 调用 tryAcquire 抢占成功 。 这个线程就只有继续park 了。

基本上来讲 有了 这个 AQS  , 我们要想实现一个 Synchronizer 就比较简单了,  最简单的情况下  treAcquire , tryRelease 实现下。AQS 里面提供的需要继承的方法不是采用abstract 的方式 而是用抛出 notImplementedException的 方式, 如果像那些tryAcquireShared 之类的 在 exclusive 模式下根本就不会被调用到,  我们也就根本就不必override。  比如 先前我们所的那个 Mutex 我们只需要定义它的内部类 Sync

Java代码

// Our internal helper class

private static class Sync extends AbstractQueuedSynchronizer {

// Report whether in locked state

protected boolean isHeldExclusively() {

return getState() == 1;

}

// Acquire the lock if state is zero

public boolean tryAcquire(int acquires) {

assert acquires == 1; // Otherwise unused

if (compareAndSetState(0, 1)) {

setExclusiveOwnerThread(Thread.currentThread());

return true;

}

return false;

}

// Release the lock by setting state to zero

protected boolean tryRelease(int releases) {

assert releases == 1; // Otherwise unused

if (getState() == 0) throw new IllegalMonitorStateException();

setExclusiveOwnerThread(null);

setState(0);

return true;

}

// Provide a Condition

Condition newCondition() { return new ConditionObject(); }

}

这个框架是非常精巧的,  还有很多地方比如那个 ConditionObject 里面涉及到另外一个单独的condition queue。只有再慢慢的啃了。

另外贴一张本人手工画的图,非常潦草多包涵了。

java concurrent 框架,java.util.concurrent 包下的 Synchronizer 框架相关推荐

  1. java.util.regex包下的Pattern和Matcher详解(正则匹配)

    java正则表达式通过java.util.regex包下的Pattern类与Matcher类实现(建议在阅读本文时,打开java API文档,当介绍到哪个方法时,查看java API中的方法说明,效果 ...

  2. 模特赛马java课程设计_Thinking in Java---Concurrent包下的新构件学习+赛马游戏仿真...

    Java5的java.util.concurrent包下引入了大量的用于解决并发问题的新类:相对于前面那些基础的线程同步和通信的方法,这些新类是一种更高层次上的抽象,使用起来还是比较容易的.这篇博客就 ...

  3. [Java多线程]-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  4. Function接口 – Java8中java.util.function包下的函数式接口

    作者:   Mohamed Sanaulla  译者: 李璟(jlee381344197@gmail.com) 早先我写了一篇<函数式接口>,探讨了Java8中函数式接口的用法.如果你正在 ...

  5. java lang保_java中lang包下的类都涉及哪几方面的

    展开全部 JDK API文档 ,   你可以下载一个 , 可以非常方便的查看类库软件包 java.lang  :  提供利用 Java 编程语言进行程序设计的基础类.32313133353236313 ...

  6. Mac本如何运营php框架,1、Mac系统下搭建thinkPHP框架环境

    No such file or directory 错误位置 FILE: /Library/WebServer/Documents/porsche/ThinkPHP/Lib/Core/Db.class ...

  7. 深入学习Java8 Lambda (default method, lambda, function reference, java.util.function 包)

    Java 8 Lambda .MethodReference.function包 多年前,学校讲述C#时,就已经知道有Lambda,也惊喜于它的方便,将函数式编程方式和面向对象式编程基于一身.此外在使 ...

  8. java.util.function包

    目录 Supplier 参数个数扩展 参数类型扩展 特殊变形 Function,r> 参数个数扩展 参数类型扩展 特殊变形 Consumer 参数个数扩展 参数类型扩展 特殊变形 Predica ...

  9. java.util.zip_[Java 基础] 使用java.util.zip包压缩和解压缩文件

    Java API中的import java.util.zip.*;包下包含了Java对于压缩文件的所有相关操作. 我们可以使用该包中的方法,结合IO中的相关知识,进行文件的压缩和解压缩相关操作. Zi ...

最新文章

  1. 人工智能的浪潮中,知识图谱何去何从?
  2. Java数据结构 利用双栈实现链表操作
  3. 快速搭建 Serverless 在线图片处理应用
  4. mybatis mysql 导入_MyBatis Mysql 批量插入
  5. 编写一个程序,计算|X-Y|的值,并将结果存入RESULT单元中,其中X和Y都为带符号字数据。
  6. 有没有编码的知识图谱_没有人告诉您关于学习编码的知识-以及为什么如此困难...
  7. 前端学习(1425):同步异步概述
  8. 掌控谈话~标注对方的痛苦
  9. 如何应对日益膨胀的数据流量? | 技术头条
  10. 压力测试jmeter入门教程
  11. element的loading的蒙版导致滚动条消失,页面抖动
  12. 苹果id无法登陆_《英雄联盟手游》苹果id和拳头账户合并教程 苹果id如何绑定拳头账号...
  13. 12 年15省赛区大学生电子设计-微弱信号检测装置
  14. 超好用的自动化测试工具——Python脚本
  15. 【智能安防监控补光灯调光芯片方案】单节锂电降压恒流驱动芯片FP8013 最大输出3A体积小/静态功耗低/效率高/支持无频闪调光
  16. 明御安全网关(IPS)批量导入黑名单IP
  17. 《大西洋月刊》2014-2020年电子版合集| The Atlantic
  18. 数据结构(从概念到C++实现)
  19. android麦克风录音格式,Android 中使用MIC(麦克风)录音
  20. 解决适配375像素宽度667像素高度移动端方法:推荐一款非常好用的px转rem单位的VSCode插件px to rem rpx (cssrem)

热门文章

  1. STM32 LWIP TCP 数据包分包后合并,超过591字节后分会分包问题
  2. 【PC工具】更新win10关闭更新工具,接速度最快最好用的文件内容搜索工具:searchmyfiles...
  3. 0.项目运行环境和项目经理
  4. 16.matlab中各种文件的I/O操作1——load/save
  5. GitNote 基于 Git 的跨平台笔记软件正式发布
  6. mysql修改Truncated incorrect DOUBLE value:
  7. 《未来架构师》的教学范例(1)
  8. idea控制台输出乱码
  9. ITIL的一些简单感受
  10. (NO.00003)iOS游戏简单的机器人投射游戏成形记(七)