ディレクトリ
AQSの決意
I.はじめに
ブロッキング・ロックと関連シンクロナイザ先入れ先出し(FIFO)に依存している(セマフォ、イベントなど)のキューを待つを実装するためのフレームワークを提供します。このクラスは、単一の原子に依存しているシンクロナイザのほとんどの種類のための便利な根拠になるように設計された
int
状態を表す値。サブクラスは、この状態を変更保護されたメソッドを定義し、どのような状態を取得または解放された、このオブジェクトの面で何を意味するのかを定義しなければなりません。これらを考えると、このクラスの他のメソッドは、すべてのキューイングおよびブロックする仕組みを行っています。サブクラスは他の状態フィールドを維持するが、唯一のアトミックに更新することができるint
方法を使用して操作値getState()
、setState(int)
及びcompareAndSetState(int, int)
同期に関して追跡されます。
AQSはFIFOが関連するロックおよび同期ロックするための基本的なフレームワークを提供し、同期キューを使用して実装しました。基本骨格を提供するために、状態の整数表現を使用することができることがシンクロナイザです。フリー実現し、ロックを取得するように、このフォームのサブクラスを介して、書き換え保護さにAQSサブクラス方法によって完全なチームとロック機構にを持つことができるであろう。
調達していた実装同期FIFOキューで使用されるAQS理論CLH
う、AQSはCLHの基本的な実装について話しています
待機キューは、「CLH」(クレイグ、とLandin、そして*ヘーゲルステン)ロックキューの変形です。CLHロックは、通常*スピンロックのために使用されています。
第二に、基本属性と状態の同期化
static final class Node {
/** 共享模式 */
static final Node SHARED = new Node();
/** 独占模式 */
static final Node EXCLUSIVE = null;
/** 标明当前线程已经被取消 */
static final int CANCELLED = 1;
/** 线程的下一个等待线程需要被唤醒 */
static final int SIGNAL = -1;
/** 当前线程正在等待中 */
static final int CONDITION = -2;
/** 下一次的acquire方法应该被无条件的传播*/
static final int PROPAGATE = -3;
/** 当前等待状态*/
volatile int waitStatus;
/** 前驱节点*/
volatile Node prev;
/** 与上面类似 */
volatile Node next;
/** 当前node持有的线程,在构造器中初始化,在退出队列后被置为null*/
volatile Thread thread;
/** 指向当前节点的后面第一个处于CODITION状态的节点,或者为SHARED,只有对于独占式才会有CODTION节点的存 *在,对于共享式的其nextWaiter为SHARED(变量)
*/
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/** 返回前驱节点,添加一层封装*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // 用于创建出事头结点和SHARED标志的构造器
}
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;
}
}
**同期状態は5種類、0に分割されINITIAL
、1 CANCELLED
、-1 SINGAL
、-2 CONDITION
、-3のPROPAGETE**
5つの状態次の役割とステータス情報
- SIGNAL:ノードの後続ノードが経過し
LockSupport.part()
、現在のノードが解放されるか、その後継ノードがスレッド間の競合を避けるために、目を覚ますする必要が後に削除されたリソースへのアクセスをメソッドのブロックをacquire
すべてのメソッドを設定する必要がありSIGNAL
、再その後、フラグをアトミックキャプチャ操作は、買収が失敗した場合、ブロックされました - CANCELED:にタイムアウトや中断によるノード
CANCELLED
状態、ノードの状態が他の状態に入らない場合、スレッドの状態がブロックされることはありません - CONDITION:ノードがキューで待機している、彼は同期キュー(すなわち、目覚めまたは前駆体ノードされていない通常のノードとして使用されます
unpark()
、彼の状態が0に設定されていない限り、)INITIAL
- PROPAGETE:共有ロックを解除するには(
releaseShared
)他のノードに渡す必要があります。doReleaseShared
最初のノード続ける情報の普及を保証するために使用されています - INITIAL:初期状態または中間状態
状態のこれらのタイプは、よりシンプルで区別することができます場合は>0
つまりCANCELLED
、<=0
使用することができます状態
CLH同期キュー、次のチャート
- 前のノードへの先行ノードは、次のノードは後輪駆動であります
nextWaiter
フィールド、モードロック状態(モード)を保存し、tryAcquire(int)
そしてtryAcquireShared(int)
それがチームにaddWaiter(ノードモード)方法を呼び出すために失敗した場合、排他または共有モードで状態を得る方法。nextWaiterは、その現在のフォームを示すために使用されます
- SHARED列挙共有モード、新しいノード(の値)、値は、一意では使用して
static
変性 - EXCLUSIVE列挙排他モードの値
null
#predecessor()
ウェイ、ノードのノードの前にノードのノード。メソッドの内部では、Node p = prev
ローカルコピーを同時実行、回避することですprev
完了したと判断し== null
たスレッドの安全性を確保するために、修正することが発生したとき。
第三に、チームaddWaiter
3.1はじめに基本的な手順
- 新しいノードを生成します
node
新しいノードの
node
元へ前駆テール・ノードポイントtail
テール・ノードによって提供さUNSAFE
tail
新しいノードにnode
提供される最後から二番目のノードは、元のテール・ノードである
old tail
後輪駆動ノードがnext
新しいテール・ノードでありますnode
- それはで失敗した場合
enq
、再び再試行- 最初のノードがnullの場合、新しいノードが
node
ヘッドノードに提供され、 - いいえ2工程と同様の動作を行います
- 最初のノードがnullの場合、新しいノードが
チームの問題を記述するためのグラフィカルな形式を使用します
3.2 addWaiter()
深いaddWaiter()
ソース・コード・ビュー
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 先进行一次简单的入队尝试
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
3.3 enq(Node node)
enq
メソッドとメソッドaddWaiter
一貫性のある、主な違いは、ノードを考慮し、現在のノードを初期化する必要がない場合は、初期化をノードに設定されていることですhead node
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // 如果头部不存在就进行初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;//该步骤和3.2中的步骤类似
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
第四に、チーム
CLH同期キューはFIFOに従うことを、スレッドは最初のノードの同期状態を解除し、それが目を覚ますだろう次の(ノードNode.next
)。後続ノードは、同期ステータスの取得に成功となり、(それ自体がヘッドノードを設定しますhead
)。
このプロセスは非常に単純であり、head
元のノードの実装と最初のノードを切断next
し、現在のノードがprev
あってもよいです。このプロセスであることに注意のことを確認するためにCASを使用する必要があるため、一つだけのスレッドが正常に同期に入ることができます。
手順は以下の通りです。
#setHead(Node node)
上記の達成するための方法デキュー・ロジックを。コードは以下の通りであります:
private void setHead(Node node) {
head = node; node.thread = null; node.prev = null;
}
参照
- [Javaのサイケ高い同時実行] http://www.iocoder.cn/JUC/sike/aqs-0-intro/
- ダグ・リー:「Javaの並行プログラミングの本当の」