西の同期とロックのエスカレーション

序文

ジュニアパートナーこんにちはみんなは、私はジャック徐よ、今日はあなたが同期でチャットし、清明節の休日です。Benpianは、最初の並行プログラミングであり、それが最初である理由並行プログラミングに関与あまりにも多くのものが、不明瞭ので、ちょうど、知識が記事を書くことができ、このような計算を引き出します少なくとも10に並行プログラミングのシリーズを終えました。あなたを伝えるためにユーザーフレンドリーな方法が明確であると私は理解して話す、分類カテゴリをランク付けし、知識のポイントをまとめます。

なぜ同期

次のコードでは、この問題は非常に簡単です、我々初見

10000オープンスレッドは、変数カウントがインクリメントされ、その結果は、スレッドセーフがあることが明らかである、9998です。なぜこのような結果があると、答えは簡単です

这里稍微解释下为啥会得不到 100(知道的可直接跳过), i++ 这个操作,计算机需要分成三步来执行。
1、读取 i 的值。
2、把 i 加 1.
3、把 最终 i 的结果写入内存之中。
所以,(1)、假如线程 A 读取了 i 的值为 i = 0,(2)、这个时候线程 B 也读取了 i 的值 i = 0。
(3)、接着 A把 i 加 1,然后写入内存,此时 i = 1。(4)、紧接着,B也把 i 加 1,此时线程B中的 i = 1,
然后线程B 把 i 写入内存,此时内存中的 i = 1。也就是说,线程 A, B 都对 i 进行了自增,但最终的结果却是1,不是 2.
复制代码

最終的に文を使用すると、同期を追加することができ、この問題を解決するためにどのようにして、そんなに操作アトミックではありません

三つの特徴

上記の例では、その原子を実証しました。所定起こる-前に、同期コードを実行するスレッド後によれば、視認性を確保するために同期化、変数のすべてのコードの変更は、直ちに他のスレッドによって見ることができます。単語の順序は、最終的な分析、単語からダウン順次命令の並べ替え、コードブロックのコードの実行を禁止され、並行性の問題を同期する3つの機能が同期され、保証されては蛇油、必ず使用している彼!

使用

3つの用途の文法的に言えば、同期の合計:

  • 改変方法の例
public synchronized void eat(){
	.......
  .......
}
复制代码
  • 変更された静的メソッド
public static synchronized void eat(){
	.......
  .......
}
复制代码
  • 変更されたブロック
public void eat(){
   synchronized(this){
   	.......
 	.......
   }
}
复制代码
public void eat(){
   synchronized(Eat.class){
   	.......
 	.......
   }
}
复制代码

前記第一及びその他の第三、第二、第四など、これはここでは、非常に簡単ですが、同期の使用をまとめたものです:

  • ロックオブジェクトの選択は、任意のオブジェクトでもよいです。
  • ロックオブジェクトを同期ブロックではなく、自分自身のロックされています。
  • ロックオブジェクトが同じオブジェクトを一緒に保持するすべてのスレッドを使用する場合は、複数のスレッド同期コードの異なるタイプが実行されます。
  • 中括弧内のコードを同期させる必要があります。必要性は、秩序よりいずれかまたは必要なアトミック、視認性を確保するために同期することを意味しています。コード効率に影響を与える、ノーコード同期を入れないでください。

ロックのエスカレーション

まあ、これのクライマックスは、ロックのアプリケーションのリソースがユーザーモードから、システム、カーネルによって呼び出されなければならないので、同期、初期のJDKでヘビー級のロックと呼ばれる、我々は慎重に耳を傾け、来た - >カーネルモードの変換効率は、JDK1比較的低いです。ためには、いくつかの最適化6を行った後に取得するために削減し、パフォーマンスをもたらすリリースロックはオーバーヘッドバイアスロック、軽量ロックの概念を導入します。あなたが同期され、四つの状態がラッチされている中で見つけませんので:何のロックを、ロック、ロック、軽量、ヘビー級ロックする傾向があります。

私たちは、同期ロックオブジェクトを知っている図に示すように、オブジェクトは、ヒープ内のオブジェクト、オブジェクトのレイアウトです。

特定のサイズが8バイトmarkwordの前には、次の4つのバイトは、クラスポインタは、オブジェクトがどのクラスに属しているが、人々はPeople.classで、猫のカテゴリはCat.classで、クラスのインスタンスデータの後にあなたのフィールドを参照することがあります、int型の年齢は4バイトであり、文字列の名前は英語の1バイトで、2は中国のセットをバイト(バイト数では中国の文字列の使用に依存して、UTF-8タイプをエンコードし、中国は2を占めています3バイト、もしGBK型、中国語2バイト)、及び最後に前部3が8で割り切れない組み合わせを、8で割り切れることがパディングされます。ロックのエスカレーションを変更値下げフラグである内側プロファイルは、図中markword(8×8 = 64ビット)です。

2は十分ではありませんので、ときずつ前進01とそのため、オンラインマップは、32ビット、ここで私が描く64ビットであるが、我々は、5つの州の合計を発見しました。

バイアスされたロック

ホットスポットの仮想マシン上での調査は、ほとんどの場合、何のロックコードが存在しないことが判明した後、マルチスレッドの競争だけではない、と常に同じスレッドを複数回取得します。だから、確率に基づいて、我々は、スレッド同期ロックアクセスプラスコードのブロックは、最初のオブジェクトID CAS操作で頭の中で、現在のスレッドを保存しようとしたときに、バイアスされているロックをロックし始めました

現在のスレッドが正常markword IDが格納されている場合(1)、次いで、同期ブロックを作ります

(2)ロックされた、いかなる競合、スレッドポインタがちょうど同じ直接同期ブロックを行うことができるか否かを判断しない同じスレッド場合

ある場合(3)他のスレッドは、この場合のショーは、(この操作はグローバルまで待たなければならない軽量ロックとして、それを保有競争が現在のロックがあることを、あなたは、スレッドがロックをバイアスされた元に戻す必要がある、とロックをアップグレードするために、賛成を得ているロック実行するには、バイトコード実行にはスレッドではない安全性の点)

当社のアプリケーション開発では、確かに二つ以上のスレッドがあなたがロックを開く傾向があるならば、それは資源の枯渇ロックへのアクセスを強化する、ほとんどの場合で競争があるでしょう。したがって、パラメータは、JVM UseBiasedLockingバイアスロックオンまたはオフにすることによって設定することができます

軽量ロック

失効LR自身のスレッドをポイントにCAS動作markwordセットで、独自のスレッドスタック内の各スレッドがLockRecordを生成し、アップグレードロック軽量のロックをバイアス、ロックが成功し与えるために提供されます。時刻の同期ブロックを実行しているスレッドが非常に長い場合に軽量がロックプロセスにロック、スピンロックの使用は、スピンロックの使用は、実際には、一定の条件があり、その後、スレッドは、サイクルに継続しますが、消費しますCPUリソース。

PreBlockSpinを修正するために、またはCPUコアの半分以上の数のスピンスレッド数:デフォルトのスピンによって数(1)は10倍で、-XXすることができます

(2)後JDK1.6、スピンの数が固定され、同時に、スピンロックとロックの所有者の前に時間に応じていないことを適応スピンロック、適応手段の導入状態が決定します。同じオブジェクトスピンウェイトロックを保持しているだけで、正常優勝ロック、およびスレッドのロックが実行されている場合、仮想マシンは、このスピンが再び成功する可能性が高いと思いますし、それがスピンを許可します比較的長い時間を待ちます。ロックの場合、スピンはほとんどロックを取得しようとする中で、将来的に資源の浪費を避けるために、直接スレッドプロセッサをブロックし、スピン工程を省略することが可能になることを、成功していません

いずれかの後にヘビー級ロックにアップグレードこれら2例

ヘビーロック

このディスターブラファイエットまでの時間、とは、オペレーティングシステムのスケジューラを待って、待ちキューにオペレーティングシステム、Linuxのミューテックス、グレード3-0レベルのシステムからCPUコール、スレッドの懸濁液にリソースを適用し、バックユーザ空間にマッピングされました。

私達はちょうどsynchronizedキーワードで簡単なコードを記述します。まず.classファイルにコンパイルし、次にてjavap -c xxx.classの逆アセンブルを使用しています。私たちは、アセンブラ命令のJavaに対応するコードを取得することができます。これは命令の次の2行を見つけることができます。

命令二次元バイトコード重要である、monitorenter、moniterexit(注:コードブロックACC_SYNCHRONIZEDを使用し、これは符号ビットであり、根本的な原理または2つの命令)

Javaの各オブジェクトは、モニタがロック状態にあるときに占有する、モニタロックモニタに関連付けられています。monitorenter命令を実行する場合、以下のようにスレッドは、モニタの取得所有しようとします:

  • モニターに入る数が0の場合、スレッドは、モニタに入り、その後、数が1に設定されている入力、スレッドはモニタの所有者です。
  • スレッドだけに、モニターを占有している場合は、モニタープラスワンに番号を入力し、再入力してください。
  • 別のスレッドがモニタを占有している場合の数は、モニタの所有権を取得するために、再試行は、次に、ゼロになるまで、スレッドは、モニタにブロックされた状態となります。

上記の二つのプロセスからわかるように、最初:モニターロック非フェア:モニターは、再入、彼のカウンター、二です

真剣ロックのパフォーマンスに影響を与える、カーネルスレッドがブロックされている(Linux)のスケジューリング状態に入った後に、これは、システムがユーザーモードとカーネルモードの間で前後に切り替えることになります、達成するためのモニターオペレーティングシステムに依存mutexLock(ミューテックス)

ロック排除

私達はすべて重要なメソッドが同期されるように変更されているためにStringBufferは、スレッドセーフであることを知っているが、我々は上記のコードを見て、私たちは、他のスレッドがすることはできません、この参照のみaddメソッドで使用されるSB、わかります参照(それはローカル変数であるため、プライベート・スタック)、SBはJVMが自動的に内部StringBufferオブジェクトの除去をロックします、したがって、共有リソースには不可能です。

public void add(String str1,String str2){
         StringBuffer sb = new StringBuffer();
         sb.append(str1).append(str2);
}
复制代码

概要

この記事で取り上げ十分な知識ポイントが非常に明確に説明するために、同期されています。同期のJava並行プログラミングは、スレッドセーフを確保するための最も一般的な方法で、その使用も比較的簡単です。前はるかReentrantLockのより同期の性能を最適化するために、同期が、同期バイアスロックの導入以来、軽量ロック(スピンロック)した後、両者の性能差は、ほぼ同じです。両方の方法の場合に利用可能です、でも提案同期公式の使用は、実際には、私は同期最適化技術がでCAS ReentrantLockのを借りた感じ。彼らは、問題が解決するロックユーザーモードにしようとしていると回避スレッドがカーネルモードに遮断します。

おすすめ

転載: juejin.im/post/5e898b8fe51d4546cd2fda30