Javaの並行プログラミングの基礎 - 同期メカニズム

スレッドセーフなのセクションでは、我々は並行プログラミングで発生する一般的な問題を分析し、記事の最後に問題を抱えている状態同期メカニズムによって共有変数を解決するために呼ば並行性の問題を、解決する方法を述べました。

同期のコンセプト

これは、異なるスレッドの発生を制御するためのオペレーティングプログラムとの間の同期機構の相対的な順序を指します。

  • 共有メモリの同時実行モデルでは、明示的な同期が実行されます。プログラマが明示的メソッドまたはスレッド間の相互排除を実行するために必要なコードの一部を指定しなければなりません。
  • 同時実行モデルのメッセージングでは、以降のメッセージを受信する前に送信メッセージは、したがって暗黙の同期が実行されなければなりません。

Javaの並行処理は、共有メモリモデルを使用して、プログラマとの間の相互排除の実装では、コードに必要な糸のセクションの方法またはディスプレイを指定する必要があります。
同期の目的は:内部のマルチスレッドプログラミングでは、敏感な共有リソースの一部は、いつでもデータを確実にするために、同期アクセス技術を使用することで、この時間は、データの整合性を確保するためには、最大1つのスレッドのアクセスであり、複数のスレッドによる同時アクセスを許可していません。

Javaの同期方法

その上セマフォ、条件と:大まかに言えば、同期機構Javaプラットフォームは、次のような、契約下にあったロック、揮発性のキーワード、finalキーワード、待っ/のnotifyAllやツールを提供しています。一般的な同期メカニズムが記述されているところです。

ロックの概要

共有変数のための保護を提供するためにロックを使用し、スレッドへのアクセスは、データを共有する前に、適切なロックを適用する必要があります。スレッドがロックを取得すると、スレッドを保持するために言ったスレッドロックされ、ロックが唯一のスレッドが保持することができます。ロックを保持しているスレッドは、ロックによって保護された共有変数にアクセスして、訪問した後、適切なロックを解除することができます。
ロックが解除されるまで、この時間をロックし、コードの実行を取得した後にロックを保持しているスレッドが呼ばれるクリティカル領域クリティカル領域でのアクセスの共有変数をのみを許可し、重要な領域は一つのスレッドだけ実行することができます。具体的に次の図によります。

排他ロック-ロックは唯一のスレッドが保持することができます。すなわち、その一つだけのスレッドがコードのクリティカルセクションを実行することができます。
内部ロックの実現を含めたJavaプラットフォームをロックして、キーワードと明示的なロックjava.concurrent.locks.Lockインタフェースを--synchronized。

同期

任意のロックオブジェクトのJavaプラットフォームは、それに関連付けられている1、モニター(モニター)または内部ロックとも呼ばれています。synchronizedキーワードによって達成この内部ロック。次のように3つの一般的な同期方法があります。

class X {
  // 修饰非静态方法
  synchronized void foo() {
    // 临界区
  }
  // 修饰静态方法
  synchronized static void bar() {
    // 临界区
  }
  // 修饰代码块
  Object obj = new Object();
  void baz() {
    synchronized(obj) {
      // 临界区
    }
  }
}  
复制代码
  1. 修正非静的メソッドは、現在のオブジェクトのインスタンスにこれをロック
  2. 変更された静的メソッドのロックは、現在のクラスのClassオブジェクトであります
  3. コードブロックを変更すると、オブジェクトobjをロックされています

使用同期形は、彼は、オブジェクトがyesの共有変数を保護者であるロックされた人を見つける必要があります。

同期の原則

まず、我々はmonitorenterとmonitorexit 2つのバイトコード命令をマークし、以下に示す例同期逆コンパイル所定のコードを使用して、これら2つの命令は、ロックおよびロック解除を示すために、パラメータのタイプへの参照を必要としますオブジェクト。

明示的に述べられていない場合は、ロックオブジェクトとしてのクラスメソッドのClassオブジェクトを取る場合、それは、同期インスタンスメソッドまたは変更されたメソッドのクラスに基づいて決定される;同期場合、Javaプログラムは、明示的にオブジェクトを参照すること、その後、オブジェクト引数を指定しました。結論するには、ロックオブジェクトは2つのカテゴリに分類されています

  • 同期(本)、ならびに非静的同期方法は、それ自体がロックされたオブジェクトを呼び出します
  • 静的静的メソッドの修正及び同期(xxx.class)クラスのクラスオブジェクトは一つだけ、そのクラスのシェアのように、すべてのオブジェクトのロックを持っているので、クラスオブジェクトクラスがロックされています。

JVM仕様、monitorenter命令の実行は、スレッドは、オブジェクトのロックに対応する第一の参照を取得しようと

  • オブジェクトロックが所持をロックされていない場合、またはスレッドがすでにオブジェクトのロックを所有して変更する前に、ロックカウントがインクリメントされて置きます。
  • 1、によってオブジェクトロックmonitorexit命令カウンタを実行するとき、カウンタが0である際に、ロックが解除されます。ロックされた他の目的は、スレッドがオブジェクトロックの所有権を取得しようとしてブロックされました。

ロック

JavaSE5、パッケージ及びインタフェースを実装するために使用される方法の後を除いて、同期を提供し、同様の同期ロックロック機能を追加し、その取得と解放するロックを表示するだけでなく、これらのインタフェースは、同期の特性を持っていない提供するために必要な時間次の表:

プロパティ 説明
ロックを取得しようとする試みをノンブロッキング 現在のスレッドがロック解除を待ってブロックしない、この瞬間が正常に取得されたロックを保持している別のスレッドによって獲得されていない場合、ロック、ロックを取得しようとします
中断獲得ロック ロックが解除されている間、同期異なるスレッドが取得したロックのスレッドが中断された割り込みに対応するためのロックを取得することができ、割り込み例外は、スローされます
タイムアウト獲得ロック 締め切りはまだロックを取得することができない場合に返さその後、指定された期限前にロックを取得するためのインターフェイス

上記の特性に対応するコード:

// 支持中断的 API
void lockInterruptibly() throws InterruptedException;
// 支持超时的 API
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 支持非阻塞获取锁的 API
boolean tryLock();
复制代码

ReentrantLockのリエントラントロック

ReentrantLockのは、あなたがロック()メソッドを呼び出したときに、ロック・スレッドがブロックされることなく、再びロック()メソッドを呼び出すことができます取得したことを意味し再入国ロックをサポートする共通実現ロックインターフェースです。ロックを取得する際、同時に、ロックはまた、株式および非株式オプションをサポートしています。最後に、ReentrantLockのは、アクセスに一つだけのスレッドを許可すると同時に、ロック排他ロックです。株式と非株式数の注意事項について:

  • 絶対時間は、ロックを取得するための最初の要求が最初に満たされなければならない場合には、ロックが公平である、逆に非公正です。
  • 公平なアクセスロックは、ロックの最長時間スレッドの優先アクセスを待っています。ReentrantLockのコンストラクタは、それが公正なロックであるかどうかを制御します。
  • 通常の状況下では、公正なロックを保証するFIFO原理に従ってロックを取得するが、価格は、パフォーマンスの低下が生じ、スレッド切り替えがたくさんあります。むしろ公正よりスレッド不足の部分につながるが、より高いスループットを確保できました。

以下の一般的な使用パターンは、アクティブリリースロックをReentrantLockのことに注意してください:

private final Lock rtl = new ReentrantLock();
// 获取锁
rtl.lock();  
try {
  // 临界区
} finally {
  // 保证锁能释放
  rtl.unlock();
}  
复制代码

読み書きロック

同時にアクセスする複数のスレッドを許可しますが、アクセスのスレッドを書いて、すべてのスレッドを読んで、他のことができますしながら、前述のReentrantLockのは、アクセスに一度に一つのスレッドを可能にする排他ロックで読み書きをロックスレッドの書き込みがブロックされています。読み書きロックはロックや読み書きロック、ロックを保持し、ロックを読んでいる共有ロックは、複数のスレッドで同時に取得することができ、書き込みロックが排他ロックに突入サポートされています。例ReentrantReadWriteLockの書き込みロックは、次の特徴があります。

  • フェア選択:ReentrantLockのと同様の
  • 再エントリー:リエントラントロック、ライター・スレッドに特別な注意を払う書き込みロックを取得した後に再び書き込みロックを取得することができ、また読み取りロックを取得することができます。
  • ロックダウングレード:その後、書き込みロック、ロックを書き込むための読み取りロックを取得するために放し、書き込みロックを取得フォローには、読み取りロックにダウングレードすることができます
  • スレッドが読み取りロックを保持しているときはいつでも、他のスレッドがロックを書き込むための適切なロックを取得することはできません。これは、他のスレッドが読み取り共有変数の間に更新するために、スレッドを読むことはできないことを保証します
public class Cache<K, V> {
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock r = rwl.readLock();
    private ReentrantReadWriteLock.WriteLock w = rwl.writeLock();
    private Map<K, V> cache = new HashMap();

    public V getKey(K key) {
        V result = null;
        r.lock();
        try {
            result = cache.get(key);
        } finally {
            r.unlock();
        }
        if (result != null) {
            return result;
        }
        w.lock();
        try {
            result = cache.get(key);
            if (result == null) {
                // db查获取value
                V v = null;
                result = v;
                putValue(key, v);
            }
        } finally {
            w.unlock();
        }
        return result;
    }
    public V putValue(K key, V value) {
        w.lock();
        try {
            return cache.put(key, value);
        }finally {
            w.unlock();
        }
    }
}
复制代码

非スレッドセーフのHashMapこの例では、スレッドを確保する読み書きロックを使用して、バッファとして実現します。読み込み操作が同時にアクセスがブロックされていない複数のスレッドをサポートするには、共有ロックの読み取りロックを取得する必要があり解析コード、。書き込み動作では、最初の書き込みロックを取得し、書き込みロックを取得した後、他のスレッドが読んでブロックされているために書き込みロックを取得するために、ロックのみ書き込み後に解放され、他の操作を続けることができ、これはまた、すべての読み取りことを保証します最新のデータがあります。

軽量同期揮発性

視認性と秩序を確保するために:volatileキーワードは、多くの場合、彼らの役割と同じ場所の役割をロックし、軽量ロックと呼ばれています。具体的な分析は見ることができますJavaのメモリモデル、分析を詳細に説明しています。

おすすめ

転載: juejin.im/post/5d6a81d651882505a87a9ec3