シングル・インスタンス・オブジェクトの同時呼び出しには同期が必要です


単一のオブジェクトの同時呼び出しを同期する必要があるのはなぜですか?
最近『Inside theJVM』という本を読んで、日常の仕事の勉強中の感想を結びつけて、勝手に何かを書いて、トンボが水を点けて、章法を持つ必要はありません.シングル・インスタンス同期について:
単例対象の同時呼び出しに同期が必要かどうかを尋ねる人がいたが、基本的には「月経帖」に属しており、答えは既成の天下にあるが、本当に心を落ち着かせる解釈は少ない.実際,JVMの動作原理をいくつか学習すれば,この問題を説明するのは難しくない.
クラスがDAOの設計などの単一の例である場合、すべてのスレッドがクラスのインスタンスにアクセスすると、同じオブジェクトが得られることは言うまでもありません.これらのスレッドの現在の操作が「反発」である場合、各スレッドは、そのインスタンスのアクセス資格を取得するときに、操作の途中で他のスレッドに介入されて予知できない結果が生じないように、オブジェクトを現在の操作が終了するまで独占的に享受するために、オブジェクトをロックする必要があります.問題は、どのような操作が「反発」なのかということです.
簡単に言えば、反発操作は、2つの操作が共有するリソースを変更しようとしているが、変更の結果は予知できない.そこで問題は、何が「共有リソース」なのかということになります.この問題は、現在のjava仮想マシンの仕様に従っているため、純粋なjava構文の観点から説明できません.2つのスレッドが1つの単一のオブジェクトに同時にアクセスしようとしていると仮定します.たとえば、
    int method1(int i) {    
        int j = 3;     
        return i+j;    
    } 

仕様の仮想マシンスレッドはmethod 1()を呼び出すときにこのようにします.
1)  method1()     ,    ,        ;
2)           j,     3;
3)           i, j      ;
4)                   ,  method1()          。

なお、現在のスレッドスタックは現在のスレッド固有であり、他のスレッドには絶対にアクセスされません.このように、method 1()で使用されているすべてがローカル変数またはパラメータである限り、各スレッドには独自のスタックフレームがあり、それぞれコヒーレントではないため、マルチスレッドの同時呼び出しに悩む必要はありません.複雑な点はmethod 1()がこのように定義されている場合です.
int method1(int i, SingletonClass singleObj) {    
     singleObj.intValue ++;    
     int j = i + singleObj.intValue;    
     return j;    
 }    

これでスレッド同期の問題を考慮せざるを得なくなり、この方法は明らかに反発的な操作「singleObj.intValue+;」を含んでいる.前述したように、メソッドのパラメータは、メソッドが終了するまで現在のスレッドプライベートスタックに押し込まれるが、singleObjは真のオブジェクトインスタンスではなく参照アドレスにすぎないため、singleObjという参照値はスレッドプライベートスタックに押し込まれるが、真のオブジェクトインスタンスはスタックに格納され、スタックはスレッドプライベートであるが、スタックはすべてのスレッドで共有されているため、singleObjのメンバー変数intValueは、現在のスレッドが2行目のコードを実行する前に他のスレッドによって変更される可能性があります.例えば、スレッド1がmothod 1()を呼び出すときsingleObj.intValueの値は1,iの値は2であり,正しい場合,method 1()の戻り値は4であるべきである.しかし、スレッド1とスレッド2とがほぼ同時にmethod 1()を呼び出すと、スレッド2は、スレッド1がintValueを2にした直後にsingleObjを実行する.intValue++は、singleObjが一例であり、2つのスレッドが遭遇するsingleObjが同じオブジェクトであるため、今回の演算でintValueを3にする.次にスレッド1は2行目のコードを継続し,結果jの結果はi+3=2+3=5となる.このように、スレッド1がmethod 1()を呼び出す戻り結果が果たして4であるか5であるかは確定できず、運に任せて、スレッド2がスレッド1がmethod 1()を呼び出すことから戻り値を取得するまでの間に居眠りをすることを期待する.ほとんどの場合、このような「運に任せる」やり方は受け入れられません.method 1()を呼び出す間、スレッド2の干渉を受けないことをスレッド1に保証する必要があります.方法は次のとおりです.
int method1(int i, SingletonClass singleObj) {    
    int j = 0;    
    synchronize(singleObj) {    
        singleObj.intValue ++;    
        j = i + singleObj.intValue;    
    }    
    return j;    
}    

スレッド2は、スレッド1がint j=0を実行するときにsingleObjのintValueを変更する可能性が高いため、method 1()を呼び出す前にsingleObjをロックすべきである.
synchronize(singleObj) {    
    int result = obj.method1(2, singleObj);    
}    

まとめてみると、「1つの方法が共有オブジェクト(またはスタックオブジェクト)の書き込み操作に関連する場合は、そのオブジェクトを同期する必要があります」という言い方は、ほとんどの場合正しいですが、漠然とした言い方ではありません.「共有オブジェクトに対するメソッドの書き込み操作が他のスレッドの戻り値の不確実性をもたらす場合、メソッドはオブジェクトを同期する必要があります.」訂正:本明細書に記載されているタイトルは「Inside the JVM」です.この本はJava世界の経典の著作で、興味のあるネットユーザーは本名でネット上でたくさんの資料を見つけることができて、その中国語の訳名は《Java仮想機に深く入り込みます》です
変換元:http://www.xuebuyuan.com/1999663.html