IDEAS SPACE

一步步提升技术 做出你想做的事
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

AQS设计与源码分析

Posted on 2023-03-09 23:36  LKB_HUGH  阅读(11)  评论(0编辑  收藏  举报

目录

满足的需求(JSR166)

设计

实现

  • 实现的细节/选用的工具
  • 最终的交互方式(使用方式)

具体的JUC运用

  • 模板方式的使用与各自工具的区别

Guaua的实现

总结

我的尝试

引用

 

满足的需求(JSR166)

What need of the Java community will be addressed by the proposed specification?

  Low level threading primitives, such as synchronized blocks, Object.wait and Object.notify, are insufficient for many programming tasks. As a result, application programmers are often forced to implement their own higher level concurrency facilities. This results in enormous duplication of effort. Further, such facilities are notoriously hard to get right and even more difficult to optimize. The concurrency facilities written by application programmers are often incorrect or inefficient. Offering a standard set of concurrency utilities will ease the task of writing a wide variety of multithreaded applications and generally improve the quality of the applications that use them.

从JSR中JUC是为了给synchronized等并发关键字做补充的,并提供给开发者一套标准的、易用、准确的多线程工具。

 

设计

Functionality

Synchronizers possess two kinds of methods [7]:

  • at least one acquire operation that blocks the calling thread unless/until the synchronization state allows it to proceed,
  • and at least one release operation that changes synchronization state in a way that may allow one or more blocked threads to unblock

 

The package does maintain consistent conventions across classes to support a range of common usage options. When meaningful, each synchronizer supports:

• Nonblocking synchronization attempts (for example, tryLock) as well as blocking versions.

• Optional timeouts, so applications can give up waiting.

• Cancellability via interruption, usually separated into one version of acquire that is cancellable, and one that isn't.

 

Synchronizers may vary according to whether they manage only exclusive states – those in which only one thread at a time may continue past a possible blocking point – versus possible shared states in which multiple threads can at least sometimes proceed. To be widely useful, the framework must support both modes of operation.

 

The java.util.concurrent package also defines interface Condition, supporting monitor-style await/signal operations that may be associated with exclusive Lock classes, and whose implementations are intrinsically intertwined with their associated Lock classes.

 

实现

实现的细节

The basic ideas behind a synchronizer are quite straightforward. An acquire operation proceeds as:

while (synchronization state does not allow acquire) {
	enqueue current thread if not already queued;
	possibly block current thread;
}
dequeue current thread if it was queued;
update synchronization state;
if (state may permit a blocked thread to acquire) {
	unblock one or more queued threads;
}

Support for these operations requires the coordination of three basic components:

• Atomically managing synchronization state

• Blocking and unblocking threads

• Maintaining queues

 

Blocking

Until JSR166, there was no Java API available to block and unblock threads for purposes of creating synchronizers that are not based on built-in monitors. The only candidates were Thread.suspend and Thread.resume, which are unusable because they encounter an unsolvable race problem: If an unblocking thread invokes resume before the blocking thread has executed suspend, the resume operation will have no effect.

The java.util.concurrent.locks package includes a LockSupport class with methods that address this problem. Method LockSupport.park blocks the current thread unless or until a LockSupport.unpark has been issued. (Spurious wakeups are also permitted.) Calls to unpark are not "counted", so multiple unparks before a park only unblock a single park. Additionally, this applies per-thread, not per-synchronizer. A thread invoking park on a new synchronizer might return immediately because of a "leftover" unpark from a previous usage. However, in the absence of an unpark, its next invocation will block. While it would be possible to explicitly clear this state, it is not worth doing so. It is more efficient to invoke park multiple times when it happens to be necessary.

 

Queues

The heart of the framework is maintenance of queues of blocked threads, which are restricted here to FIFO queues. Thus, the framework does not support priority-based synchronization.【注释1】

These days, there is little controversy that the most appropriate choices for synchronization queues are non-blocking data structures that do not themselves need to be constructed using lower-level locks. And of these, there are two main candidates: variants of Mellor-Crummey and Scott (MCS) locks [9], and variants of Craig, Landin, and Hagersten (CLH) locks [5][8][10]. Historically, CLH locks have been used only in spinlocks. However, they appeared more amenable than MCS for use in the synchronizer framework because they are more easily adapted to handle cancellation and timeouts, so were chosen as a basis. The resulting design is far enough removed from the original CLH structure to require explanation.

 

 

注释

注释1:这里写着不支持优先队列,其实公平锁与非公平锁并不是优先级锁。

注释2:In the original versions of CLH locks, there were not even links connecting nodes. In a spinlock, the pred variable can be held as a local. However, Scott and Scherer[10] showed that by explicitly maintaining predecessor fields within nodes, CLH locks can deal with timeouts and other forms of cancellation: If a node's predecessor cancels, the node can slide up to use the previous node's status field.

The main additional modification needed to use CLH queues for blocking synchronizers is to provide an efficient way for one node to locate its successor. In spinlocks, a node need only change its status, which will be noticed on next spin by its successor, so links are unnecessary. But in a blocking synchronizer, a node needs to explicitly wake up (unpark) its successor.

引用

JSR166

https://gee.cs.oswego.edu/dl/papers/aqs.pdf