JAVAメモリモデルノート(JMM)

JAVAメモリモデル

ここで作成されたメモは、JVMのJavaメモリモデルと組み合わされます。

Javaとの並行プログラミングの技術におけるJavaメモリモデル

いくつかのインタビューの質問を組み合わせる

JVMメモリー領域とJAVAメモリーモデルには明確な違いがあります

それらの間の関係を区別するには

JMMは、主に並行マルチスレッドメモリの可視性を研究するためのルールです

キャッシュが読み取りおよび書き込みアクセスを行うためのプロセスの抽象化です。

目的は、メモリが一貫したメモリ

ここに画像の説明を挿入

メインメモリとワーキングメモリ

Javaメモリモデルは、各スレッドの作業メモリを作成します

ただし、すべての変数はハードウェアのメモリではないメインメモリに格納する必要があります。

しかし、JVMの一部
ここに画像の説明を挿入

揮発性の記憶セマンティクス

セマンティクス1揮発性変数への変更は他のスレッドにすぐに表示されます

しかし、揮発性変数を使用した計算ないスレッドセーフ

たとえば、マルチスレッドが揮発性変数をロックせずにインクリメントすると、同期エラーが発生します

なぜなら

Num ++は、実際には4つのバイトコード操作で構成されています。スタックの一番上の番号は、他のスレッドによって増加する可能性があります。

問題解決:

1. AtomicIntegerインクリメント操作はincrementAndGet()メソッドを使用できます

2.メソッド同期

使用シナリオ:

1.操作結果が現在の値に依存しない、またはset getメソッドなどのシングルスレッドの変更が元の値に依存しない

2.変数は、変数制約に参加するために他のスレッドを必要としません

Semantic 2 Volatileは命令の並べ替えの最適化を禁止しています

volatitle boolean  config = false ;

finishconfig(config);
   
config = true;

while(!config){
    sleep()}

dosomethings()

configが揮発性変数でない場合

configはfhinishconfigよりも高速かもしれません

これにより、構成前に他のユーザーが実行される可能性があります。

揮発性変数の3つの特性を要約する

読み取りと書き込み自体はアトミックであり、他のものは自己増加しません原子性

Volatile変数の操作はすべてのスレッドに表示され、キャッシュは強制的に更新されます 可視性

揮発性変数を並べ替えてメモリバリアを追加することはできません 整然とした

メモリのセマンティクスをロックする

ロックの解放と獲得は本質的にメッセージ通知です

スレッドがロックを解放すると、次のスレッドにそのスレッドのロックメッセージを取得するように指示します。

スレッドがロックを取得すると、実際にはスレッドからロックメッセージを受け取ります。

同期は基本的にロックとロック方法に似ています

1つのスレッドのみが実行されるため、同期化された操作はアトミックです原子性

同期化された操作がすべてのスレッドに表示された後、変数のロックを解除すると、変数がメインメモリに同期されます可視性

実行中のスレッドが1つしかないため、同期されたコードの順序を変更できません 整然とした

一部のロックの実装は、本質的には、CAS操作の揮発性変数を変更して、メモリの一貫性を実現することです。

最終フィールドのメモリセマンティクス

finalはロックおよび揮発性とは異なります

最終的なフィールドアクセスは通常の変数に似ています

しかし、最後のフィールドは2つの並べ替え規則に従う必要があります

  1. コンストラクターの最後のフィールドの書き込みと、このオブジェクトの後続の参照は、参照変数に割り当てられます。この操作は並べ替えできません
  2. 初めて最終フィールドへの参照を読み取り、その後この最終フィールドを読み取る場合、この操作は再注文できません
// 一个对象
static Ex object;
// 构造函数
public Ex(){
    // i 是 final域
    i=0;
    // j 是普通域
    j=2;
}
// A线程写
public void write(){
    object = new Ex()}
// B 线程读
public void read(){
    Ex obj  = object ;
    int a = obj.i;
    int b = obj.j;
}

上記の例を見てください

Bスレッドは、コンストラクターの初期化後に、最後のフィールドであるobj.iを読み取る必要があります。

共通フィールドであるobj.jの場合、初期値は必ずしも読み取られる必要はありませんが、デフォルトの初期値である場合があります。

最後のフィールドは、オブジェクトの値または参照が適切に初期化される必要があることを保証します

最終フィールドは、オブジェクトの最終フィールドが読み取られる前に、オブジェクトの参照が読み取られる必要があることを保証します

前に起こる

先行ルールは、データが競合しているかどうか、およびスレッドが安全かどうかを判断するための重要な原則です。

  1. プログラムシーケンスは、シングルスレッド内のシーケンスフローで実行されます。
  2. ロックがロックされる前に、チューブロック(モニターロック)がロックを解除します。
  3. 揮発性の原則は、変数を読み取る前に揮発性変数を書き込みます
  4. スレッド開始の原則スレッド開始メソッドstart()は、スレッド内のすべての操作の前に実行されます
  5. スレッド終了の原則Thread.join endメソッドなど、スレッドのすべての操作はスレッド終了の前に実行されます。
  6. スレッドの中断の原則は、スレッドの中断された()操作が、中断されたスレッドでの割り込みイベントの検出に先行することです。
  7. オブジェクトのファイナライズの原則オブジェクトの構築メソッドは、そのファイナライズメソッドに先行します。
  8. 推移性、AはBの前、BはCの前、AはCの前

ダブルチェックロックDCL

DCLと呼ばれるダブルチェックロックは、非常に一般的な遅延読み込みテクノロジです。

つまり、オーバーヘッドが大きい一部のオブジェクトでは、使用時に読み込まれます。オンデマンドでロードします。遅延読み込み

以下はスレッドセーフでない初期化オブジェクトです

public class LazyInital {
    class Instatnce{
        
    }
    private static Instatnce instatnce;
    
    public Instatnce getInstatnce(){
        if(instatnce==null)      // Thread A
            instatnce = new Instatnce();  //  Thread B
        return instatnce;
    }
}

簡単な変更は、同期したロゴをgetInstanceメソッドに追加することです。

しかし、このパフォーマンスは良くありません

以下はDCLの間違ったバージョンです

public Instatnce getInstatnce(){
    if(instatnce==null){
        synchronized (LazyInital.class){
            if(instatnce==null)
                instatnce = new Instatnce();
        }
    }
    return instatnce;
}

これはダブルチェックロックと呼ばれ、非常に快適に見えますが、大きな問題があります。

Javaオブジェクトの初期化は3つの部分に分かれています

  1. memory = allocate()//メモリを割り当てます
  2. initclass(memory)//オブジェクトを初期化する
  3. instance = memory //オブジェクトを指す

2と3は再注文される場合があります。

最後の状況は

スレッドAはインスタンスを初期化していません。スレッドBは既にオブジェクトへの参照を取得しています。現時点では、初期化されていないオブジェクトを返します。

解決策

  1. オブジェクトの初期化とポイントされたオブジェクトの並べ替えを許可しない

Volatileのセマンティクスを使用してVolatileの
説明追加する

  1. スレッドAオブジェクトの初期化は並べ替えることができますが、スレッドBからは見えないようにする必要があります。

クラス初期化ソリューションを使用し、JVM初期化クラスオブジェクトロックの特性に依存する

public class InstanceFactory {
    static class Instance{

    }
    // instanceHolder类的初始化加锁
    private static class InstanceHolder{
        private static Instance instance = new Instance();
    }
    public static Instance getInstance(){
        return InstanceHolder.instance;
    }
}

揮発性と同期の違い

  1. volatileの本質は、作業メモリー内の現在の変数の値が不確かであり、メインメモリーから読み取る必要があることをjvmに伝えることです

    同期は現在の変数をロックします。これは1つのスレッドのみが読み取ることができ、他のスレッドはロックが解放されるまでブロックされます。

  2. 揮発性変数レベル、同期メソッド変数

  3. Volatileは変数の変更の可視性のみを実現でき、操作の原子性を保証できません。たとえば、Volatileの読み取りと書き込みはアトミックですが、Volatileの操作は必ずしもアトミックで自己拡張的ではありません。

  4. 揮発性はブロックせず、同期はブロックします

  5. volatitle変数は最適化されず、同期が最適化されます

元の記事を22件公開 Likes2 Visits 881

おすすめ

転載: blog.csdn.net/weixin_41685373/article/details/105085104