ホームページ  >  記事  >  Java  >  JavaのSynchronizedの原理は何ですか

JavaのSynchronizedの原理は何ですか

王林
王林転載
2023-05-31 14:55:141704ブラウズ

    ソースコードレベルの解析 Synchronized

    オブジェクト構造

    Synchronized は Java における暗黙的なロックであり、取得ロックと解放ロックは両方ともこれは暗黙的であり、JVM の操作に完全に委ねられています。Synchronized キーワードを理解する前に、最初に学習すべき知識ポイントは Java オブジェクトの構造です。これは、Synchronized ロックは Java オブジェクトに格納されているためです。Java オブジェクトの構造は次のとおりです。 :

    JavaのSynchronizedの原理は何ですか

    Java オブジェクトは、オブジェクト ヘッダー、インスタンス データ、フィル データの 3 つの部分で構成されていることが明確にわかります。次に、オブジェクト構造の簡単な分析を行います。

    • マークダウン: オブジェクト マーク フィールドは 8 バイトを占め、オブジェクト マークに関する情報を格納するために使用されます。ロックのマークビット. 図から、ハッシュ値、軽量ロックフラグ、バイアスロックフラグなどが存在することがわかります。

    • Klass ポインタ: クラス オブジェクトの型ポインタ。現在のオブジェクトが属するクラスへのポインタです。デフォルトでは、jdk1.8 は圧縮ポインタをオンにし、4 を占有します。圧縮ポインタをオフにします。圧縮ポインタは 8 バイトを占有します。

    • オブジェクトの実際のデータ: この部分にはオブジェクトのすべてのメンバー変数が含まれます。サイズは各メンバー変数によって決まります。たとえば、byte は 1 バイト、int は 4 バイトを占め、等

    • 記入してください: この部分はスペースを補完するためのものであり、プレースホルダーとして機能します。これは、HotSpot 仮想マシンのメモリ管理システムでは、オブジェクトの開始アドレスが次である必要があるためです。は 8 バイトの整数倍であるため、オブジェクト インスタンスが整列していない場合は、パディングする必要があります。

    マークダウンロックタイプのマークには、ロックなし、偏りロック、軽量ロック、重量ロック、GCマークの計5種類があることが分かります。したがって、2 ビットのマークのみを使用すると完全に表現できないため、バイアス ロック マークが導入されます。つまり、001 はロックなし、101 はバイアス ロックを意味します。

    Monitor オブジェクト

    オブジェクト構造は上で紹介したとおりで、Mark-down にさまざまなロック情報が格納されることがわかります。ロック状態がヘビーウェイト ロック (10) の場合、マークダウンは、Monitor オブジェクトへのポインターを保存します。この Monitor オブジェクトは、モニター ロックとも呼ばれます。

    同期の動作メカニズムは、JVM が共有オブジェクト内のさまざまな競合状況を検出すると、適切なロック実装に自動的に切り替えます。この切り替えがロックのアップグレードまたはダウングレードです。 (ロックはアップグレードのみ可能でダウングレードはできないと多くの場所で述べられています。実際、この記述は間違いです。書籍「The Art of Java Concurrent Programming」では、バイアスされたロックの場合、ロックにダウングレードできると記載されています。フリー状態、およびバイアスされたロックの取り消しと呼ばれます)。

    現在、バイアス ロック、軽量ロック、重量ロックという 3 つの異なるモニター実装があります。スレッドがモニターを保持すると、ロックを取得します。

    Monitor in Java は、C の ObjectMonitor に基づいて実装されています。その主なメンバーは次のとおりです:

    • _owner: ObjectMonitor オブジェクトを保持するスレッドへのポイント

    • #_WaitSet: スレッド キューを待機状態に保存します。つまり、wait() メソッドを呼び出すスレッドです。

    • ##_EntryList: スレッド キューを待機中のロックに保存します。ブロック状態
    • _count: _WaitSet _EntryList 内のノード数のおおよその合計
    • _cxq: ロックを競合する複数のスレッドが最初に保存しますこの一方向のリンク リスト
    • _recursions: 再エントリの数を記録します
    • _object: 保存されたモニター オブジェクト
    • Get Monitor オブジェクトのスレッドが _owner 領域に入ると、_count は 1 になります。スレッドが wait() メソッドを呼び出すと、Monitor オブジェクトが解放され (ロックが解除され)、_owner が元の状態に戻ります。空および_count-1。この時点で、スレッドは _WaitSet キューに入り、起動されるのを待ちます。

    上記の説明からわかるように、synchronized キーワードを使用してロックを取得する鍵は、各オブジェクトのオブジェクト ヘッダーにあります。これは、synchronized() 括弧内に格納されているオブジェクトがロックを取得できる理由も説明しています。 特徴。

    同期機能

    原子性

    原子性とは、操作が完了したか、または完了していないことを意味します。半分完了した、つまり操作が完了していないということはありません。可能です。中断されます。

    synchronized は、同時に 1 つのスレッドだけがロックを取得し、コード ブロックに入ってコードを実行することを保証します。これが理解できない場合は、次のシーンを想像してください。トイレは1つのピットで、複数人が一緒にトイレに行くという非文明的な現象を防ぐために、トイレにも施錠されています。トイレを使用する人は全員、トイレ管理者に支払いをしなければなりません。支払い後、トイレに行く前にロックを受け取ることができます。トイレを使用した後は、トイレ管理者に鍵を返却することができます。同期はトイレ管理者なので、一度に 1 人だけが鍵を取得でき、トイレ使用後は全員が鍵を返却する必要があります。

    次に、次の同期追加メソッドを参照してください:

    public static void add() {
        synchronized (Demo.class) {
            counter++;
        }
    }

    それをデコンパイルしてコードを表示します:

    javap -v -p Demo

    public static void add();
        descriptor: ()V
        flags: ACC_PUBLIC, ACC_STATIC , ACC_SYNCHRONIZED
        Code:
          stack=2, locals=2, args_size=0
             0: ldc           #12                 // class
             2: dup
             3: astore_0
             4: monitorenter
             5: getstatic     #10                 // Field counter:I
             8: iconst_1
             9: iadd
            10: putstatic     #10                 // Field counter:I
            13: aload_0
            14: monitorexit
            15: goto          23
            18: astore_1
            19: aload_0
            20: monitorexit
            21: aload_1
            22: athrow
            23: return
          Exception table:

    次のことができます。明らかにモニターに関連する 2 つの命令があることに注意してください:

    • #monitorenter: 同期フラグ ACC_SYNCHRONIZED があると判断した後、最初にこのメソッドに入ったスレッドが最初に Monitor の所有者になります。このときのカウンタは 1

    • monitorexit: 実行時、終了後、カウンタは -1 になり、0 に戻り、他の開始スレッドによって取得されます。

    Visibility

    Visibility は、を参照します。複数のスレッドが同じ変数にアクセスするとき スレッドがこの変数の値を変更すると、他のスレッドはそれを即座に感知し、変更された値を確認できます。スレッドの可視性は JMM と密接に関係しています。次の記事では、volatile キーワードを使用して可視性を実現する方法を検討します。

    そして、Synchronized にはロックとロックの解放に関する次のセマンティクスがあるため、可視性があります:

    • スレッドがロックする前に、メインメモリから共有変数の最新の値を読み取るために、作業メモリ内の共有変数の値をクリアする必要があります。

    • スレッドがロックを解放すると、共有変数の値がメイン メモリに更新される必要があります。

    • synchronized の可視性は、オペレーティング システムのカーネル ミューテックスの実装に依存します。これは、JVM のロックとロック解除に相当します。コード ブロックを終了するときは、共有変数を更新する必要があります。この点 volatile キーワードとは異なり、volatile キーワードの可視性はメモリ バリア (メモリ バリアとも呼ばれます) に依存します。

    順序性

    as-if-serial は、コンパイラとプロセッサがパフォーマンスの最適化のために命令をどのように並べ替えても、それが保証される必要があることを保証するためのものです。シングルスレッド

    での実行結果の正確性。つまり: このスレッド内で観察すると、すべての操作は順序どおりですあるスレッド内の別のスレッドを観察すると、すべての操作は順序どおりではありませんここでの順序付けは volatile とは異なることに注意してください。命令の並べ替えを防ぐために volatile ではありません。

    リエントラント ロック

    リエントラント ロックの概念は非常に単純です。つまり、スレッドは保持しているオブジェクト ロックを複数回取得できます。この種のロックはリエントラント ロックです。ロックには、同じ数のロックを解放する必要もあります。同期ロックオブジェクトには、ロックを取得した回数、つまりリエントラントの数を記録するためのカウンタがあります。

    ロック アップグレードのプロセス

    同期ロックには、ロックなし、バイアスされたロック、軽量ロック、および重量ロックという 4 つの代替アップグレード状態があります。これらの状態は、競合によって徐々にエスカレートします。

    完全なロックアップグレード図は後で追加されます

    以上がJavaのSynchronizedの原理は何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。