j。u。c包有一个LockSuport类,这个类中包含了解决这个问题的方法。方法LockSupport。park阻塞当前线程除非/直到有个LockSupport。unpark方法被调用(unpark方法被提前调用也是可以的)。unpark的调用是没有被计数的,因此在一个park调用前多次调用unpark方法只会解除一个park操作。另外,它们作用于每个线程而不是每个同步器。一个线程在一个新的同步器上调用park操作可能会立即返回,因为在此之前可能有“剩余的”unpark操作。但是,在缺少一个unpark操作时,下一次调用park就会阻塞。虽然可以显式地消除这个状态,但并不值得这样做。在需要的时候多次调用park会更高效。
这个简单的机制与有些用法在某种程度上是相似的,例如Solaris-9的线程库,WIN32中的“可消费事件”,以及Linux中的NPTL线程库。因此最常见的运行Java的平台上都有相对应的有效实现。(但目前Solaris和Linux上的Sun Hotspot JVM参考实现实际上是使用一个pthread的condvar来适应目前的运行时设计的)。park方法同样支持可选的相对或绝对的超时设置,以及与JVM的Thread。interrupt结合 —— 可通过中断来unpark一个线程。
3。3 队列
整个框架的关键就是如何管理被阻塞的线程的队列,该队列是严格的FIFO队列,因此,框架不支持基于优先级的同步。
同步队列的最佳选择是自身没有使用底层锁来构造的非阻塞数据结构,目前,业界对此很少有争议。而其中主要有两个选择:一个是Mellor-Crummey和Scott锁(MCS锁)[9]的变体,另一个是Craig,Landin和Hagersten锁(CLH锁)[5][8][10]的变体。一直以来,CLH锁仅被用于自旋锁。但是,在这个框架中,CLH锁显然比MCS锁更合适。因为CLH锁可以更容易地去实现“取消(cancellation)”和“超时”功能,因此我们选择了CLH锁作为实现的基础。但是最终的设计已经与原来的CLH锁有较大的出入,因此下文将对此做出解释。
CLH队列实际上并不那么像队列,因为它的入队和出队操作都与它的用途(即用作锁)紧密相关。它是一个链表队列,通过两个字段head和tail来存取,这两个字段是可原子更新的,两者在初始化时都指向了一个空节点。