Java concurrencyの公平ロック動力ノードJava学院の整理


公平ロック(JDK 1.7.0_に基づく。40)
1.ロック()
unlock()はReentrant Lock.javaで実現されたソースコードは以下の通りです。

public void unlock() {
  sync.release(1);
}
説明:
アンロック()はAQSのrelease()関数によって実現されるアンロック関数です。
ここでは、「1」という意味は、「ロックを解除した状態」を設定するパラメータです。「公平ロック」は重入可能なので、同じスレッドに対してはロックを1回解除するごとにロック状態-1となります。
AQSについては、Reentrant Lockとsyncの関係は以下の通りである。

public class ReentrantLock implements Lock, java.io.Serializable {

  private final Sync sync;

  abstract static class Sync extends AbstractQueuedSynchronizer {
    ...
  }

  ...
}

そこから、私たちはsyncがRentrant Lock.javaのメンバーオブジェクトであり、SyncはAQSのサブクラスであることを発見しました。 
2.リリース()
release()はAQSで実現されました。ソースは以下の通りです。

public final boolean release(int arg) {
  if (tryRelease(arg)) {
    Node h = head;
    if (h != null && h.waitStatus != 0)
      unparkSuccessor(h);
    return true;
  }
  return false;
}
説明:
release()は、先にtryRelease()を呼び出して、現在のスレッドロックが持つロックを解除してみます。成功すると、後続のスレッドを起動してtrueに戻ります。そうでなければ、直接falseに戻ります。 
3.tryRelease()
tryRelease()はReentrant Lock.javaのSync類で実現され、ソースは以下の通りです。

protected final boolean tryRelease(int releases) {
  // c           
  int c = getState() - releases;
  //   “    ”  “     ”,     !
  if (Thread.currentThread() != getExclusiveOwnerThread())
    throw new IllegalMonitorStateException();

  boolean free = false;
  //   “ ”           ,   “ ”     null,        。
  if (c == 0) {
    free = true;
    setExclusiveOwnerThread(null);
  }
  //            。
  setState(c);
  return free;
}

説明:
tryRelease()はロックを解除する試みです。
(01)「現在のスレッド」が「鍵の所有者」でない場合、異常を投げかける。
(02)「現在のスレッド」が今回のリリースロック操作後にロックの所有状態が0(つまり、現在のスレッドはこの「ロック」を完全に解放する)である場合、「ロック」の持ち主はnullであり、つまりロックは取得可能状態である。また、現在のスレッドのロックを更新した状態は0です。
get State()、set State()は前の章で紹介しましたが、ここでは説明しません。
get Exclusive Ownerantread()、set Exclusive Ownerantread()はAQSの親クラスAbstractOwnable Syncronizer.javaで定義されています。ソースは以下の通りです。

public abstract class AbstractOwnableSynchronizer
  implements java.io.Serializable {

  // “ ”     
  private transient Thread exclusiveOwnerThread;

  //   “      ” t
  protected final void setExclusiveOwnerThread(Thread t) {
    exclusiveOwnerThread = t;
  }

  //   “      ”
  protected final Thread getExclusiveOwnerThread() {
    return exclusiveOwnerThread;
  }
  
  ...
}

4.unparkSuccess or()
release()において「現在スレッド」がロック解除に成功すると、現在のスレッドの後継スレッドが呼び覚まされます。
CLHキューのFIFOルールによれば、「現在スレッド」(すなわちロックを取得したスレッド)は、必ずヘッドである。CLH列が空でないと、ロックの次の待ちスレッドが起動されます。
次にunparkSuccess orのソースコードを見てみます。これはAQSで実現されます。

private void unparkSuccessor(Node node) {
  //          
  int ws = node.waitStatus;
  //     <0,     =0
  if (ws < 0)
    compareAndSetWaitStatus(node, ws, 0);

  //       “       ”,    ,   for      。
  //      ,  “           <=0”
  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);
}

説明:
unparkSuccess orの役割は「現在のスレッドの後継スレッドを呼び覚ます」というものです。後続スレッドが呼び覚まされると、このロックを取得して運転を再開することができます。
node.waitStatusの説明については、「前の章でNode類についての紹介」を参照してください。
 締め括りをつける
「解除錠」のプロセスは、「鍵を取得する」プロセスに対して比較的簡単である。ロックを解除する時、主に行われる操作は、現在のスレッドに対応するロックを更新した状態です。現在のスレッドのロックが完全に解除されている場合、「ロック」を持つスレッドはnullとなり、現在のスレッドの状態を空にして、後続のスレッドを起動します。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。