[Java同時シリーズ]3.Javaのロック

4859 ワード

J.U.Cパッケージのlocksの下のクラス(インタフェースを含む)について説明します.
ロックは主に複数のスレッドが共有リソースにアクセスすることを制御するための方法であり、通常、1つのロックは、同じ時間内に複数のスレッドが共有リソースに同時にアクセスすることを防止することができる(読み書きロックを除いて、読み書きロックは同じ時間内にあり、複数の読み書きロックが同時に共有リソースを読むことを許可する).
1.ロックコネクタ
ロックインタフェースはsynchronizedキーワードと同様に、同期機能を提供しています.しかしロックが使用される場合、明示的にロックを取得する必要があります.ロックはsynchronizedと比較して暗黙的にロックを取得する利便性を失ったが、ロックの取得と解放を制御することができ、ロックとタイムアウトロックを中断することができる.
2.ロックインタフェース主要API
  • void lock(); 取得ロック
  • void lockInterruptibly(); lockInterruptiblyを使用してロックを取得すると、現在のスレッドを中断できることと、lock()メソッドとの違い
  • boolean tryLock(); ブロックされていない取得ロックを試み、このメソッドを呼び出した後、取得できる場合はtrueを返し、取得できない場合はfalseを直接返します.
  • boolean tryLock(long time, TimeUnit unit); タイムアウト取得ロック
  • void unlock(); リリースロック
  • Condition newCondition(); 待機通知コンポーネントを取得します.このコンポーネントは現在のロックにバインドされており、現在のスレッドがロックされている場合にのみ、コンポーネントのwait()メソッドを呼び出すことができ、wait()メソッドを呼び出すと、現在のスレッドはロックを解放します.

  • 3.ロックインタフェースの実装クラス
    1.ReentrantLock(再ロック)
    再入ロックとは、再入をサポートするロックであり、このロックがリソースに対するスレッドの重複ロックをサポートしていることを示します.
    再アクセス:任意のスレッドが取得ロックでブロックされずに再取得できることを意味します.公平と非公平取得ロック:公平とは、絶対時間において、ロックを要求するスレッド(待ち時間が最も長いスレッドがロックを優先的に取得する)が最初にロックを取得することを意味し、このロックは公平であり、逆に非公平である.
    ①. ロックの再入力
    ロックの再アクセスを実現するには、次の2つの問題を解決する必要があります.
  • ロックの取得:ロックを取得するには、ロックを取得したスレッドがこのロックを取得したスレッド(すなわち、現在のスレッドがこのロックを占有しているかどうか)であるかどうかを確認する必要があります.以下のコードは、非公平にロックを取得する方法
  • である.
    final boolean nonfairTryAcquire(int acquires) {
                //      
                final Thread current = Thread.currentThread();
                //        
                int c = getState();
                if (c == 0) {
                //    
                    if (compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                //               
                else if (current == getExclusiveOwnerThread()) {
                    //       
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
    
    

    この方法では、まず、このロックが占有されているか否かを判断し、そうでなければCAS方式で同期状態を設定する.ロックが占有されている場合、現在のスレッドがロックを占有しているスレッドであるか否かを判断し、取得操作が成功したか否かを決定し、ロックを取得したスレッドが再びロックの取得を要求すると、同期状態値が増加しtrueに戻る.したがって、再入ロックの取得は、スレッドの再入に成功した場合に、ロックの同期状態値を大きくすればよい.
  • ロックの解放:スレッドがNという取得ロックを繰り返すと、N回解放され、他のスレッドがロックを取得することができる.次のコードは、ロックを解放するコードです:
  • protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }
    

    ロックがスレッドによってN回取得された場合、前(N−1)回はfalseが返され、同期状態が終了して解放されると(c=0)、占有スレッドがnullに設定されてtrueが返されます.
    ②. 公平と非公平の取得ロック
    次に、ロックを公平に取得するコードを示します.
    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;
            }
        }
    

    上記の非公正取得ロックのコードと比較して、このコードではif条件にhasQueuedPredecessors()メソッドが1つだけ追加されている.これは、同期キューにおいて、現在のノードに前駆ノード(すなわち、現在のスレッドよりも早い取得ロックのスレッド)があるか否かを判断するため、hasQueuedPredecessors()がtrueに戻ると、ロックの取得を続行するには、前駆スレッドがロックを取得して解放するのを待つ必要があります.
    2.ReadWriteLock(読み書きロック)
    排他ロックとは、同じ時点で1つのスレッドのみが読み書きロックにアクセスできることを意味します.同じ時間に複数の読み書きスレッドがアクセスできるようにし、書き込みスレッドがアクセスすると、読み書きスレッドと他の書き込みスレッドがブロックされます.読み書きロックは、1つの読み書きロックと1つの書き込みロックを維持し、読み書き分離によって同時性能を向上させる(少なくとも排他ロックよりも性能が向上する).
    //todo読み書きロックの内容が多いので、後で書きます
    4.LockSupport類
    LockSupportクラスはJ.U.C.locksパッケージにあり、主にいくつかの共通の静的メソッドを定義しており、これらのメソッドは最も基本的なスレッドブロックと起動機能を提供しています.次の表は、LockSupportで提供されている方法と説明です.
    方法
    説明
    public static void park()
    現在のスレッドをブロックし、他のスレッドがunpark()を呼び出したり、現在のスレッドを中断したりした場合にpark()メソッドから返すことができます.
    fipublic static void parkNanos(long nanos)
    現在のスレッドをブロックし、nanosナノ秒を超えると自動的に戻ります.
    public static void parkUntil(long deadline)
    現在のスレッドをdeadline時間までブロック
    public static void unpark(Thread thread)
    ブロックされたスレッドの起動