Javaスレッド(3)の理解について話す--------キーワード同期

同期されたキーワードと言えば、誰もがよく知っていると思います。コードでこのキーワードを使用することはめったにないかもしれません。結局のところ、シングルトンモードはカプセル化されています。マルチスレッドの同時実行性の問題がある場合は、通常、分散ロックを使用します。 、リソースのプリエンプションはすべてデータベースまたは他の共有リソースの場合であり、現在の展開は分散されているため、通常のコードで同期される役割ははるかに少ないように見えます。

ただし、誰もがこのフィールドをよく目にします。たとえば、ArrayListがスレッドセーフであることがわかっています。Vectorがスレッドセーフであるのはなぜですか?ああ、同時の追加と削除を防ぐために同期ロックが追加されていることがわかりました。HashMapはスレッドセーフです。安全ではありません。同時ハッシュ衝突が発生した場合、linkedListの操作とほぼ同等であり、追加時に問題が発生する可能性があります。ConcurrentHashMapがスレッドセーフなのはなぜですか?ああ、CAS操作と同期ロックの使用によるものであることが判明しました。 HashMapで発生する可能性のある問題を回避しながら、詳細なロックを調整するためのスロット。

さて、一緒に同期されたキーワードを見てみましょう。

同期とは、一般に、メソッド、静的メソッド、コードブロックをロックすることを意味し、クラスをロックすると言われることもあります。実際、クラスレベルのロックとメソッドレベルのロックの2種類があります。コードを見てみましょう:

1.同期コードブロックをロックし、オブジェクトをロックします。

public class SyncThread implements Runnable {

    private Integer number = 0;

    public void method() throws Exception{
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+"-synchronized锁同步代码块:"+number++);
                Thread.sleep(10);
            }
        }

    }

    @Override
    public void run() {
        try {
            method();
        } catch (Exception e) {

        }
    }
}

実行してみましょう

    public static void main(String[] args) {

        SyncThread syncThread = new SyncThread();
         Thread thread1 = new Thread(syncThread, "SyncThread1");
         Thread thread2 = new Thread(syncThread, "SyncThread2");
         thread1.start();
         thread2.start();
    }

SyncThreadオブジェクトを作成し、SyncThreadを介して2つのスレッドを作成して開始しました。つまり、両方のスレッドが同じ番号で動作します。もちろん、最初のスレッドはロックされ、実行が完了して、次のスレッドが続行されます。実行すると、結果は次のようになります。

SyncThread1-synchronized锁同步代码块:0
SyncThread1-synchronized锁同步代码块:1
SyncThread1-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:3
SyncThread1-synchronized锁同步代码块:4
SyncThread2-synchronized锁同步代码块:5
SyncThread2-synchronized锁同步代码块:6
SyncThread2-synchronized锁同步代码块:7
SyncThread2-synchronized锁同步代码块:8
SyncThread2-synchronized锁同步代码块:9

一部のパートナーは驚かれることでしょう。1つのスレッドに2つの代替実行と累積をスレッド化しないのはなぜですか。同じメソッドを操作して実行可能を実装します。これは、メソッドに入った後に同期されたスレッドのブロックに相当します。代替実行はどこにありますか?

もちろん、交互に実行したい場合は、2つのSyncThreadを作成するだけです。


         Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
         Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
         thread1.start();
         thread2.start();

実行したら、clang。

SyncThread1-synchronized锁同步代码块:0
SyncThread2-synchronized锁同步代码块:0
SyncThread2-synchronized锁同步代码块:1
SyncThread1-synchronized锁同步代码块:1
SyncThread2-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:2
SyncThread2-synchronized锁同步代码块:3
SyncThread1-synchronized锁同步代码块:3
SyncThread2-synchronized锁同步代码块:4
SyncThread1-synchronized锁同步代码块:4

スレッド1とスレッド2は独自のことをしていることがわかりました。元々は2つのクラスですか?ロックはメソッドであり、変数は通常の変数です。2つのオブジェクト間に競合はありません。これらはオブジェクトであることに注意してください。では、どのようにしてそれらを競争力のあるものにしますか?つまり、ロックはクラスロックにアップグレードされます。最も簡単な方法は、オブジェクトへのバインドと同等の静的な番号を番号に追加することです。すべてのクラスはこの番号で動作します。もちろん、現時点では、コードブロックだけを実行し続けると、間違いなく同時の質問が発生します。ロックは、クラスロックまたは静的オブジェクトロックに上昇する必要があります。コードを見てみましょう。

 private static Integer number = 0;

    public void method() throws Exception{
        synchronized (number) {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+"-synchronized锁同步代码块:"+number++);
                Thread.sleep(10);
            }
        }

    }

次に、2つのオブジェクトに対して一種のテストメソッドを実行します。

SyncThread1-synchronized锁同步代码块:0
SyncThread2-synchronized锁同步代码块:1
SyncThread2-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:3
SyncThread2-synchronized锁同步代码块:4
SyncThread1-synchronized锁同步代码块:5
SyncThread2-synchronized锁同步代码块:6
SyncThread2-synchronized锁同步代码块:7
SyncThread1-synchronized锁同步代码块:8
SyncThread1-synchronized锁同步代码块:9

Process finished with exit code 0

それでも同期されている場合(これ)、同時実行の問題が発生するため、自分で試すことができます。

2.ロック方法

    public synchronized void method() throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "-synchronized锁同步代码块:" + number++);
            Thread.sleep(10);
        }
    }

操作の結果を見てみましょう:

SyncThread1-synchronized锁同步代码块:0
SyncThread2-synchronized锁同步代码块:1
SyncThread2-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:3
SyncThread2-synchronized锁同步代码块:4
SyncThread1-synchronized锁同步代码块:5
SyncThread2-synchronized锁同步代码块:6
SyncThread1-synchronized锁同步代码块:7
SyncThread2-synchronized锁同步代码块:8

Process finished with exit code 0

このとき、lockメソッドとlock同期コードブロックが実際には同じレベルにあることがわかります。どちらもメソッド内のループをロックし、共同でリソース番号を取得します。

静的メソッドはどうですか?

    public static synchronized void method() throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "-synchronized锁同步代码块:" + number++);
            Thread.sleep(10);
        }
    }

静的メソッドは明らかにクラスレベルのロックです。操作の結果を確認できます。

SyncThread1-synchronized锁同步代码块:0
SyncThread1-synchronized锁同步代码块:1
SyncThread1-synchronized锁同步代码块:2
SyncThread1-synchronized锁同步代码块:3
SyncThread1-synchronized锁同步代码块:4
SyncThread2-synchronized锁同步代码块:5
SyncThread2-synchronized锁同步代码块:6
SyncThread2-synchronized锁同步代码块:7
SyncThread2-synchronized锁同步代码块:8
SyncThread2-synchronized锁同步代码块:9

Process finished with exit code 0

スレッドがリソースを占有すると、スレッドはこのメソッドの完了を待ってからリソースを解放します。実際、ロックされるのはこのクラスです。

もちろん、これも効果です。


    public synchronized void method() throws Exception {
        synchronized (SyncThread.class) {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + "-synchronized锁同步代码块:" + number++);
                Thread.sleep(10);
            }
        }
    }

さて、同期ロックについては大まかに理解しましたが、同期はどのようにしてロックメカニズムを実現するのでしょうか。

オブジェクトがヒープ内に作成されることは誰もが知っています。また、メモリ内のオブジェクトのストレージレイアウトは、オブジェクトヘッダー、インスタンスデータ、および配置パディングの3つの領域に分割できます。その中で、オブジェクトヘッダーにはロック情報が含まれています。以下は、オブジェクトヘッダー自体の実行データです。

コンテンツを保存する フラグビット 状態
オブジェクトのハッシュコード、オブジェクトの生成年齢 01 ロックされていません
ロックレコードへのポインタ 00 軽量ロック
ヘビーウェイトロックへのポインタ 10 ヘビー級ロック
空気 11 GCマーク
スレッドIDバイアス、タイムスタンプバイアス、生成年齢 01 バイアスをかけることができます

 

もちろん、型ポインターもあり、JVMはこのポインターを使用して、オブジェクトがどのクラスインスタンスであるかを判別します。

同期ロックは、実際には楽観的ロックと悲観的ロックの混合ロックです。楽観的ロックが最初に処理されます。失敗した場合は、悲観的ロックが使用されます。悲観的ロック、つまりヘビーウェイトロックは、モニターを介して実装されます。同期オブジェクトロック、そのポインターはモニターオブジェクトのアドレスを指し、各オブジェクトにはモニターがあり、モニターはオブジェクトと一緒に作成および破棄できます。または、スレッドがロックを取得しようとしたときに自動的に生成されます。モニターはC ++で実装されています。興味のある人はそれを探索できます。ここでは説明しません。

上記の同期コードブロック(javap)をロックする方法をコンパイルして表示します。monitorenterとmonitorexitが実際にロックしてロックを解除していることがわかります。monitorenter命令を実行するときは、最初にオブジェクトロック、つまりモニターオブジェクトの取得を試みる必要があります。オブジェクトがロックされていない場合、または現在のスレッドがこのオブジェクトのロックをすでに所有している場合は、ロックの値を増やします。もちろん、モニターが解放されると、モニターは1ずつ減少します(同期はOKリエントラントです)。

さて、それはすべて同期についてです、そこに何か問題があります、コメントでそれを指摘することができます、それは非常に感謝しています!

 

犠牲も勝利もありません!

おすすめ

転載: blog.csdn.net/zsah2011/article/details/107946415