`
whitesock
  • 浏览: 484414 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Inside AbstractQueuedSynchronizer (3)

阅读更多

Inside AbstractQueuedSynchronizer (1)

Inside AbstractQueuedSynchronizer (2)

Inside AbstractQueuedSynchronizer (3)

Inside AbstractQueuedSynchronizer (4)

 

3.4 Template Method

    AbstractQueuedSynchronizer提供了以下几个protected方法用于子类改写

protected boolean tryAcquire(int arg)
protected boolean tryRelease(int arg)
protected int tryAcquireShared(int arg)
protected boolean tryReleaseShared(int arg)
protected boolean isHeldExclusively()

    这几个方法的默认实现是抛出UnsupportedOperationException,子类可以根据需要进行改写。

 

    AbstractQueuedSynchronizer中最基本的acquire流程的相关代码如下:

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

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

    如果tryAcquire失败,那么当前线程可能会被enqueue到WaitQueue,然后被阻塞。 shouldParkAfterFailedAcquire方法会确保每个线程在被阻塞之前,其对应WaitQueue中的节点的waitStatus被设置为Node.SIGNAL(-1),以便在release时避免不必要的unpark操作。此外shouldParkAfterFailedAcquire还会清理WaitQueue中已经超时或者取消的Node。需要注意的是,在某个线程最终被阻塞之前,tryAcquire可能会被多次调用。

 

    AbstractQueuedSynchronizer中最基本的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;
}

private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}

    release方法中,总是总head节点开始向后查找sucessor。只有当该sucessor的waitStatus被设置的情况下才会调用unparkSuccessor。unparkSuccessor方法中首先清除之前设置的Node.waitStatus,然后向后查找并且唤醒第一个需要被唤醒的sucessor。需要注意的是,if (s == null || s.waitStatus > 0)这个分支中,查找是从tail节点开始,根据prev引用向前进行。在Inside AbstractQueuedSynchronizer (2)   中提到过,Node.next为null并不一定意味着没有sucessor,虽然WaitQueue是个双向链表,但是根据next引用向后查找sucessor不靠谱,而根据prev引用向前查找predecessor总是靠谱。

 

3.5 Fairness

    到目前为止我们已经知道,WaitQueue是个FIFO的队列,唤醒也总是从head开始。但是AbstractQueuedSynchronizer却并不一定是公平的(实际上,大多数情况下都是在非公平模式下工作)。如果在看一遍acquire方法会发现,tryAcquire的调用顺序先于acquireQueued,也就是说后来的线程可能在等待中的线程之前acquire成功。这种场景被称为barging FIFO strategy,它能提供更高的吞吐量。

 

    大多数AbstractQueuedSynchronizer的子类都同时提供了公平和非公平的实现,例如ReentrantLock提供了NonfairSync和FairSync。例如其FairSync的tryAcquire方法如下:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

   tryAcquire方法返回true的条件之一是!hasQueuedPredecessors() 。hasQueuedPredecessors的代码如下:

public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

    综上, FairSync优先确保等待中线程先acquire成功。但是公平性也不是绝对的:在一个多线程并发的环境下,就算锁的获取是公平的,也不保证后续的其它处理过程的先后顺序。

 

    既然默认情况下使用的都是NonfairSync,那么FairSync适合什么样的场景呢?如果被锁所保护的代码段的执行时间比较长,而应用又不能接受线程饥饿(NonfairSync可能会导致虽然某个线程长时间排队,但是仍然无法获得锁的情况)的场景下可以考虑使用FairSync。对于ReentrantLock,在其构造函数中传入true,即可构造一把公平锁。

5
1
分享到:
评论
1 楼 xiaoZ5919 2012-05-15  
我看了源码 觉得 shouldParkAfterFailedAcquire()是首先将前任节点的waitStatus设置为SIGNAL,下次循环时block该线程

  *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.



  int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)//下次循环判断 返回true block线程
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);//第一次设置,标志需要这样一个信号
        }
        return false;

相关推荐

    CLRInsideOut.zip

    3. 考虑到.NET和C/C++之间的类型系统差异,如指针和引用的处理,确保在转换过程中正确处理这些差异。 4. 注意平台兼容性问题,确保生成的C#代码能在目标平台上正常工作。 总的来说,"CLRInsideOut"是一个非常实用的...

    ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.002

    Allegro自带的ODB++inside工具下载,ODB++inside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_...

    ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.006

    Allegro自带的ODB++inside工具下载,ODB++inside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_...

    ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.004

    Allegro自带的ODB++inside工具下载,ODB++inside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_...

    ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.003

    Allegro自带的ODB++inside工具下载,ODB++inside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_...

    inside.the.c++.object.model.pdf_inside_C++_

    3. **类型系统**:C++的类型系统是其强大之处,但也带来了一些复杂性。书中深入讨论了类型转换、静态类型与动态类型、类型信息的获取等话题,帮助读者理解类型在编译期和运行期的作用。 4. **继承与多态**:C++的...

    Microsoft Excel 2010 Inside Out 无水印原版pdf

    Microsoft Excel 2010 Inside Out 英文无水印原版pdf pdf所有页面使用FoxitReader、PDF-XChangeViewer、SumatraPDF和Firefox测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细...

    ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001

    Allegro自带的ODB++inside工具下载,DBinside插件可以将Allegro的.brd文件转化为仿真工具Hyperlynx使用的文件。共6个文件,需要分别下载。 ODB_Inside_Cadence_Allegro_111_Windows_64_SA_Setup.zip.001 ODB_Inside_...

    Inside_NAND_Flash_Memories.pdf

    《Inside NAND Flash Memories》这本书由Rino Micheloni、Luca Crippa和Alessia Marelli共同编写,并由Springer出版社出版。本书详细介绍了NAND闪存的工作原理和技术细节,对于理解NAND闪存在现代数字系统中的作用...

    CLRInsideOut2008_01

    3. **Marshaling**:处理不同类型之间的转换,如.NET的托管类型到非托管类型的映射。 4. **Struct Marshaling**:对于包含非托管数据类型的结构体,需要指定如何正确地布局和传递数据。 5. **Handle and Pointer ...

    ODB++_Inside及说明.rar

    3. **灵活的约束管理**:通过ODB++,设计师可以定义并传达复杂的制造约束,如阻抗控制、平面分割等。 4. **强大的电气规则检查**:ODB++支持电气规则检查,可以在早期阶段发现和修复设计问题。 5. **自动化制造...

    Inside游戏效果文档

    文档标题为《Inside游戏效果文档》,描述了PlayDead公司在制作游戏《Inside》时所采用的渲染技术。文档内容涉及了游戏中的雾效、HDR泛光(bloom)以及其他与光照和渲染相关的高级技术。这些技术对于创造游戏的独特...

    Inside SQLite(SQLite技术内幕) 原版+个人翻译版

    《Inside SQLite》这本书深入探讨了其内部工作原理和技术细节。 首先,我们来看看“深入sqlite.docx”。这可能是作者个人对SQLite的深入理解和研究,可能包含了SQLite的架构解析、存储机制、查询优化、事务处理、...

    inside-ole英文版

    inside ole 英文版,还是英文版看着舒服!inside ole 英文版,还是英文版看着舒服!inside ole 英文版,还是英文版看着舒服!inside ole 英文版,还是英文版看着舒服!inside ole 英文版,还是英文版看着舒服!...

    Inside Windows Debugging.pdf

    本书《Inside Windows Debugging》由Tarik Soulami撰写,得到了微软公司的授权,由O’Reilly Media, Inc.出版。该书结合最佳实践和常见的调试与编程技巧,很多技巧在其他书籍中尚未被记录下来。在本书的整个阅读过程...

    ODB++_Inside_Cadence_Allegro_Windows64.zip

    3. 可扩展性:支持元器件属性、注释等附加信息,方便后期处理。 4. 互操作性:被多个EDA厂商和制造系统支持,促进了跨平台合作。 总的来说,"ODB++ Inside Cadence Allegro"结合了Allegro的强大设计功能和ODB++的...

    MicrosoftExcel2013InsideOut.pdf 英文原版

    Microsoft Excel 2013 Inside Out

    ODB++_Inside_Cadence_Allegro_114_Windows_64_SA_Setup.7z

    ODB++ Inside Cadence Allegro 11.4 Windows 64 SA Setup是一个专门针对Cadence Allegro设计平台的官方插件,主要用于支持ODB++数据格式的输入和输出。ODB++是一种广泛使用的电子设计自动化(EDA)数据交换标准,它...

    I-BUS Inside Inside the BMW Cars entertainement Serial Bu

    3. 内容部分 内容部分的文件通过OCR扫描识别,虽然可能包含一些文字识别错误,但其内容大致涉及以下几个技术要点: - 介绍和警告(Introduction and warning):文档可能以介绍I-BUS系统的基本概念开始,并可能对...

    Inside JVM

    Inside JVM

Global site tag (gtag.js) - Google Analytics