一、JAVAスレッドの実行メモリモデル【JAVAメモリモデル】について


プログラム、コレクションはあくまでメモリデータの操作であり、計算の結果を永続する.論争
JAVAで実行する最小単位はスレッドである.JVMは各CPU、オペレーティングシステム等の差異を実現する.スレッドの実行モデルは、最終的に抽象的に次のように見ることができます.
各スレッドには独自のwork memoryがあり、main memoryを共有しています.
JMMの主な問題は以下の通りである.
原子性、原子レベルの操作、各スレッドの実行時は互いに独立しており、volatileと宣言されていない変数も独立しているが、work memoryとmain memoryの同期が行われる.
可視性、スレッド間の通信.すなわち、メインメモリの変数が表示され、work memoryからmain memoryに値を同期してスレッド間の通信を行い、synchronizeまたはvolatile信頼性を通過する.
秩序性、ここでは主に主なメモリの秩序を読み取り、書き込み、同期するために、スレッドの操作は一般的に
read and loadは、プライマリメモリから現在のワークメモリuse and assign実行コードに変数をコピーし、共有変数値store and writeを変更してワークメモリデータでプライマリメモリ関連コンテンツをリフレッシュする
ここでread and loadとstore and writeはJVMによって複数回実行されます
 
1,各スレッドで実行される変数はmain memoryのcopyからキャッシュに格納される.
メモリに追加され、プライマリメモリに同期されていないなど、共通の同時発生の問題.
package org.benson.threadgroup;



import java.util.concurrent.CountDownLatch;

/**

 * TODO share the thread memory operation 

 * @author Benson QQ 107966750

 *

 */

class Data{

    public static int count=0;

}

public class TreadTest extends Thread{

    private CountDownLatch countDCH=null;

    public TreadTest(CountDownLatch countDCH) {

        this.countDCH=countDCH;

    }

    @Override

    public void run() {

        for(int i=0;i<10000;i++)

//            synchronized (Data.class) {

                Data.count++;

//            }

        countDCH.countDown();

    }

    

    public static void main(String[] args) throws InterruptedException {

        final int threadSize=10;

        CountDownLatch countDCH=new CountDownLatch(threadSize);

        for(int i=0;i<threadSize;i++)

        new Thread(new TreadTest(countDCH)).start();

        countDCH.await();

        System.out.println(Data.count);

    }

}

しゅつりょく
91660

ここでは、読み書きがプライマリメモリにタイムリーに同期されていないため、予想値100000未満であり、同期ロックが解放されると正常になります.
第2スレッドの実行が完了すると常に正確な計算値を出力し、原理はsynchronizeキーワードを見る.
2,JAVA呼び出しコードの順序は無秩序である.(DCLなどの問題)
一、bad実践、変数スレッド通信に頼って、これは再現しにくいが、Nパスを実行したのは正しいか、JVMの気持ちの問題にかかって、コードは以下の通りである.
package org.benson.threadgroup;

/**

 * TODO share load variable sort

 * @author Benson QQ107966750

 * 

 */

class Process4SortA{

    public int variableA=0;

    public Process4SortA() {

        variableA=100;

    }

}

class Process4SortB extends Thread{

    public boolean startFlag=false;

    public int variableA;

    public void test4SortRun(){

        //do something another stuff

        variableA=100;

        startFlag=true; //maybe will process this code before the variableA=100

    }

    @Override

    public void run() {

        while(true){

            if(startFlag){

            System.out.println(variableA);

            //do something another stuff

            break;

            }

        }

    }

}

public class TreadTestSort {

    public static void main(String[] args) throws Exception {

        Process4SortB proB=new Process4SortB();

        proB.start();

        proB.test4SortRun();

    }

}

 
コード出力が0に等しい場合があります.メソッドtest 4 SortRun()では、実行の順序が一定ではないため
二、bad実践、DCLの問題、ここで最もよく見られるdouble checked load(DCL)問題を引き出したのも、無秩序によるものである.
class Foo { 

private Resource res = null ; 

public Resource getResource() { 

if (res == null ) 

res = new Resource(); 

return res; 

} 

}

関連資料はたくさんありますが、実は
res = new Resource();
とみなす
Resource()
res=Resourceアドレス参照;
これは正しく実行される順序です.無秩序の原因は
res=Resourceアドレス参照;
Resource()
だから他はresを手に入れた!=null、実行しました.構造関数は全然実行されていません.
三、bad実践、単例モデル
これはDCLと似ていますJAva Singletonのいくつかの方式の解析(本当に元の帖が見つからないで、BSの下で転載して住所を貼らない)
もちろん最後のvolatileは可能です.volatileは毎回メインメモリの最新の値を読むからです.
関連キーワード
synchronize 1,monitor,2を取得して掛け,main memory copyの最新値3から実行4,work memory copyの最新値からmain memory 5までmonitorを解放する
 
final
可変ではなく、初期化時にのみ値を割り当てることができます.
しかし、非静的は構造方法に値を割り当てることができ、適用時に順序に注意し、以下のようにfinal変数を呼び出すことも可変になる.
次のコードで証明します.
package org.benson;/** * TODO to share the java load class sort * @author Benson QQ 107966750 * */class Base{ public Base() { System.out.println("init base,the variableA is "+((Child)this).variableA); } public void display(){ System.out.println("display in base,the variableA is "+((Child)this).variableA); }}class Child extends Base{ final int variableA; public Child(){ variableA=100; System.out.println("init child,the variableA is "+this.variableA); this.display(); }}public class Test4Sort { public static void main(String[] args) { new Child(); }}
結果は次のとおりです.
init base,the variableA is 0init child,the variableA is 100display in base,the variableA is 100
これでfinal値も可変になります
JAVAの初期化手順base and child class adress,static--> base class variable-->base class constructor-->child class variable-->child class constructor
volatile
スレッドでは、読み取りと書き込みは原子的であり、volatileのベストプラクティスはsynchronizeで書き込みをロックすること(または現在のvolatileの値に依存しない書き込みを使用すること)であり、読み取りはロックを使用せずに最新の値を保証することができ、共有ロックの実装と同様に
1,保証はスレッドの可視性,すなわち毎回得られるのは最新値であり,明らかに書き込みは保証されないためカウンタはできない.
2、もし1つのスレッドの中で読み書きが同時に発生するならば、それは書くことが先に読むことを保証することができて、例えばa=new instance();まずinstance()を初期化してaに値を割り当て、完全なinstanceを返すことを保証できます.
1つのスレッドの書き込みが少なく、1つのスレッドが読み取り続けると、変数がvolatileされ、最新の値が読み出されることが保証されます(ただし、書き込みは現在の値に依存しない必要があります).この場合はsynchronizeと同時に使用することが考えられます.ConcurrentHashMap類から高同時プログラムの設計構想を学ぶ【JDKソースコードに深く入り込む】参照
 
参考資料
二重チェックロックおよび単一モード
Javaメモリモデル(投稿が見つからない)
Javaマルチスレッドの発展の概要
 
Javaマルチスレッドと同時プログラミングのテーマ