[アップグレード] ---同期並行プログラミングロック+ JDK1.6他のsynchronizedキーワードの最適化入門

送信元アドレス:https://github.com/nieandsun/concurrent-study.git


synchronizedキーワードの最適化の1 JDK1.6概要

前の記事「[]並行プログラミング-バイトコード命令の観点から、synchronizedキーワードの原理を理解するために」、「 [] 並行プログラミング-さらにソースJVM原理の観点からsynchronizedキーワードを理解するには、」JDK1で導入されました0.6処理ロジックをされる前に、キーワードを同期只要线程想进入ブロック同期就会行くに调用内核函数関連付けられているモニターロックオブジェクトの所有権をつかむにします。

カーネル関数は、システムリソースを大量に消費するカーネルモードとユーザーモードに関連する問題を切り替えます呼び出し、プロセス効率を低下させます。そして、研究は、ほとんどの場合、コードが交互に行われることが示されている交替执行就不会产生并发,自然也就不会带来并发安全问题,この機構JDK1.6がsynchronizedキーワードの前に特定の問題があるので。

ダグ・リーは、ReentrantLockの事実、この問題に良い解決策を作った、あなたは私の記事を見ることができ、「[並行プログラミング] - ReentrantLockの分解能1出典:処理ロジックの同期方法を交互に実行します。」

synchronizedキーワード、右のHotSpot仮想マシンの開発チームはJDK1.6のこのバージョン偏っロック(偏ったロック)を含むロック最適化の様々な技術を達成するために多くの労力に費やさ、光理由JDK元のキーワードであるので、それはする必要がありますミドルロック(軽量ロック)、適応スピン(アダプティブスピニング)、ロック(ロックの除去)を排除し、ロック粗大化(ロック粗大化)、など - 「これらの技術は、スレッド間でより効率的に共有データに設計されており、問題これにより、プログラムの効率を高める、競争を解決します。


2同期ロックのエスカレーションプロセス

アップグレードプロセスは、ロックです:ロックなし - >バイアスロック - >軽量ロック - >ロックヘビー級


(バイアスされたロック)付勢2.1ロック - 同じスレッドが同期コードブロックに繰り返される場合に適用されます


偏ったロックとは何か2.1.1

ホットスポットは、研究の著者らの後にその実践を見つけたので偏っロックは、6 JDKを導入することが重要です大多数情况下同じスレッドで複数回だけでなく、マルチスレッド化競争の不在をロックし、常に取得低価格、偏ったロックの導入を取得するために、スレッドをロックするために。

「バイアス」のバイアスされたロックが偏心している「部分」「部分」ひいき、それは、スレッドIDがオブジェクトヘッドロックバイアスに格納されるロックした後、そのスレッドを取得するために最初に有利にバイアスされることを意味しあなただけがスレッドが出入りシンクブロックが偏っロックされ、ロックフラグとのThreadIDが可能か確認する必要があります。

しかし、それはすぐに消費がパフォーマンスはあまり消費パフォーマンスCASアトミック操作、良いよりもそれ以外の多くの害よりでなければならないロックバイアス前に失効が保存されて競争が、複数のスレッドをロックバイアス登場などとして撤回しなければなりません。


+ REVOKE原則2.1.2バイアスロックロック

[ロック]
最初のスレッドがロックし、アクセスシンクブロックを取得する際に、以下のように、バイアスされたロックプロセスです。

  • (1)仮想マシンの意思フラグオブジェクトヘッダがモードバイアスされていること、「01」に設定されています。
  • (2)IDは、操作が成功した場合、CASの間でマークWordオブジェクトに記録されたロック・スレッドに到達するためにCAS操作を使用し、将来的には、仮想マシンのあなたは、関連するシンクブロックに入るたびにバイアスされ、ロックねじロックを保持していますもはや同期運転することができ、高効率のロックを付勢しません。

マーク・ワード(私はブログの記事で見ることができる「と組み合わせること- Javaオブジェクトの元のレイアウトは非常に証明することができます!!! []並行プログラミング」) ストレージ構造をよりよく理解することができます
ここに画像を挿入説明


[取り消し]
次のようにバイアスされたロック無効化プロセスです。

  • (1)元に戻すグローバルセキュリティポイントを待たなければならない操作をロックする付勢さ(一般的にループの先端のような、JVMによって決定される、セキュリティドットの方法戻ります)
  • (2)は、バイアスされたロックスレッドのハングを持つオブジェクトがロック状態にロックされているか否かを判断します
  • 状態(3)失効付勢ロック、無回復するロック(フラグが01である)、または軽量ロック(00フラグ)

[+ロック失効プロセス概略図】
バイアスロックロック+ REVOKE原理を次の図で表すことができる。
これは、描画かなり良い、ここ☺☺☺それをもたらした塗装の工程の説明での「Java並行プログラミングの技術」です。
ここに画像を挿入説明

2.1.3偏ったロック検証

Java 6の中にバイアスされ、ロックはデフォルトで有効になっていますが、アプリケーションは数秒を開始した後にのみアクティブになった後、あなたは使用することができ-XX:BiasedLockingStartupDelay=0、それがすべてのロック・アプリケーションは、通常の状況下で競争力のあるモードで決定された場合、あなたがすることができ、遅延オフパラメータをXX:-UseBiasedLocking=falseバイアスされ、ロック・パラメーターを閉じます。

次のように検証手順は以下のとおりです。

package com.nrsc.ch1.base.jmm.syn_study.upgrade;
import org.openjdk.jol.info.ClassLayout;
public class BiasedLockingDemo {
    
    private static class MyThread extends Thread {
        //static修饰只会初始化一次
        static Object obj = new Object();

        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                synchronized (obj) {
                    //打印锁对象的布局
                    System.out.println(ClassLayout.parseInstance(obj).toPrintable());
                }
            }
        }
    }

    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
    }
}
  • 所望の効果を得るためには、実行時に、次のVMパラメータを追加する必要があります "
-XX:BiasedLockingStartupDelay=0
  • 結果は以下の通りであります:

ここに画像を挿入説明

スレッドスレッドIDとエポックを格納する旧56マークワードの緑色の部分は、56ビットの値が同じである見ることができる
最後の三つ101マークワードの黄色部分

この結果は、2.1.2で説明した表と一致しています。


バイアスされ、ロックの2.1.4利点

同期ブロックを実行するための唯一のスレッドが同じロックを取得し、繰り返しのスレッドに適用されたときにバイアスされたロックは、さらにパフォーマンスを向上させることです。競争せずに同期してバイアスされ、ロックは、パフォーマンスを向上させることができます。

これは、プログラムのロックのほとんどは、常に異なるスレッドの数あるスレッドプールのようなアクセスする場合、常にプログラムを実行することが有益ではない、つまり、また最適化された特性を持つ利点のトレードオフであるバイアスモードそれは冗長です。

JDK5では、ロックはデフォルトで無効になっていますが、偏ったロックでJDK6がデフォルトで有効になってい偏っ。しかし、唯一のアプリケーションは、数秒を開始した後、あなたが使用することができますアクティブに-XX:BiasedLockingStartupDelay=0、それは競争力のあるモードで決定され、すべての通常によって、アプリケーションをロックされている場合、遅延を閉じるパラメータをXX:-UseBiasedLocking=falseバイアスされたロック・パラメータを閉じます。


概要

  • バイアスされたロック原理:

ロックオブジェクトは、最初のスレッドが取得している場合、仮想マシンはモードを付勢され、そのヘッドが「101」に設定されているフラグを受けることになります。CAS操作が成功した場合、同時にCAS操作の使用はIDがロックにマークWordオブジェクトに記録されたロック・スレッドに到達するために、未来はあなたが、仮想マシンができ、関連するシンクブロックに入るたびにバイアスされ、ロックねじロックを保持していますもはや同期動作、高効率ロックを付勢しません。

  • 利点は、ロックする傾向があります

同期ブロックを実行するための唯一のスレッドが同じロックを取得し、繰り返しのスレッドに適用されたときにバイアスされたロックは、さらにパフォーマンスを向上させることです。競争せずに同期してバイアスされ、ロックは、パフォーマンスを向上させることができます。


代替同期方式にスレッドに適用される - 2.2軽量ロック(軽量ロック)


軽量ロックとは何か2.2.1

軽量は、「軽量」の名前は、伝統的なロック機構が「ヘビー級」ロックと呼ばれ、伝統的なロック・モニター・用語の使用に関連していると付け加えたJDK 6で新しいロックロック機構です。ニーズが強調されることをまず最初に:軽量ロックはヘビー級のロックを交換することを意図していません。

軽量ロックを導入する目的:マルチスレッドの場合には、交互に起因する重量級のロックにパフォーマンス上のオーバーヘッドを回避するために、シンクブロックを行ったが、複数のスレッドが同時にクリティカル領域を入力すると、ロックがアップグレード拡大しまし軽量原因になりますヘビー級のロックは、とても軽量な代替ヘビー級ロック可能にするロックはありません。


2.2.2 +取り消さ軽量ロックロック原則

[ロック]
機能または複数のスレッドをロックするバイアス閉じたときにバイアスされ、ロックロックに軽量リードにアップグレード偏っロックを競う、それは次のステップ、軽量ロックを取得しようとします。

  • (1)現在のオブジェクトが(ハッシュコード、0、01)は、ロック状態ではない、そしてもしそうであれば、最初のJVMがロックを格納するための現在のスレッドのスタックフレームにロック(ロックレコード)と呼ばれる空間を確立するか否かを判断します現在のマークのWordコピーのオブジェクト(公式プットこのコピープラス変位プレフィックス、すなわち変位マーク・ワード)が、現在のオブジェクトへのスタックフレームロックのレコード、所有者のポイントでロックReocrdにマークWordオブジェクトをコピーします。
  • 成功した競争がロックを表現する場合、ロックレコードポインタを指すように更新マークのWordのCASの操作対象を使用する(2)JVMの試みは、ロックフラグは00になり、同期動作を行います。
  • (3)次にである現在のスレッドが既にロックオブジェクト、同期コードブロックの直接実装を保持している場合、現在のオブジェクトは、現在のスレッドのスタックフレームにマークワードポイントを失敗したと判定された場合、そうでない場合、ロックオブジェクトのみ記載されています他のスレッドは、ヘビー級のロックを必要とする、ロックフラグがブロックされた状態になりますスレッドの後ろに待機して、10になり、軽量ロックに拡大し、差し押さえます。

[取り消し]
軽量ロックは操作は、以下の手順で行われるCASによって解放されます。

  • (1)軽量ロック変位マークワードに格納されたデータを取得するために取り出します。
  • CAS動作によって取り出された(2)現在のマークワードデータオブジェクト、成功した場合、成功したリリースロックを置き換えます。
  • (3)CASの交換作業が失敗した場合、他のスレッドがロックを取得しようとすると、あなたは軽量ロックインフレヘビー級ロックにアップグレードする必要があります。

[+ロック失効プロセス概略図】
ここでは、まだ、図中の「Java並行処理のアートを」借りる:☺☺☺。
ここに画像を挿入説明


2.2.3軽量ロック検証

それを自分自身を試して興味、予期しない結果を得ることができます。


2.2.4軽量ロックの利点

軽量ロックのために、その性能をベースに“对于绝大部分的锁,在整个生命周期内都是不会存在竞争的”、ブレークがコストの排他的根拠に加えている場合は、追加のCAS操作があるので、競争の複数のスレッドの場合は、ヘビー級のロックロックよりも軽量遅くなります。

場合シンクブロックが交互に行われるマルチスレッドヘビーロック性能は、消費のために回避することができます。


2.3スピンロック

私たちは、あなたが既に知っている必要がある寝具の以前の記事を、モニターのロック機能はカーネルスレッド公園とunparkを、すなわちスレッド公園とスイッチバックにunparkを必要と述べCPUのユーザーモードとカーネルモードスイッチを呼び出して使用することを考えています。仕事の重い負担としてCPUに頻繁に公園やunparkを、これらのオペレーティングシステムの同時パフォーマンスは、多くの圧力をもたらすでしょう。

同時に、仮想マシンの開発チームはまた、多くのアプリケーションでは、データはロックされた状態を共有することに注意し只会持续很短的一段时间、この時間は、スレッドをウェイクアップすると、ブロッキングは、それだけの価値はありません。物理マシンを2つ以上のスレッドを並列に実行することができます複数のプロセッサを持っている場合、我々はその要求スレッドロックの後ろに取得することができます“稍等一下”,が、それはロックを保持しているスレッドであるかどうかを確認するために、プロセッサの実行時間をあきらめないでくださいすぐにロックを解除します。スレッドを待機させるために、我々はできる让线程执行一个死循环(自旋)、この技術は、スピンロックとして知られています。- 「スピンロックはJDK 1.4.2で導入されていますが、デフォルトではオフになってきた、あなたは-XXを使用することができます:+ JDK 1.6で開くようにパラメータをUseSpinningデフォルトで有効になっているに変更されました。

自旋等待不能代替阻塞そして、私はプロセッサの数には話さないだろうスレッド切り替えのオーバーヘッドなしものの、自身をスピン待って、必要ですが、ロックが、短時間スピン待ちの効果を占領しているそうだとすれば、それは、プロセッサ時間を取ることです非常に良いことでしょう。逆に、ロックが長時間占有されている場合。その後、スレッドがプロセッサリソースの唯一の無駄な消費をし、任意の有用な作業を行うことはありませんが、不要なパフォーマンスをもたらすでしょうスピン。そのため、スピン待機時間は、スピンロックの数の制限は、まだ成功していない超えている場合、あなたがスレッドをハングアップする伝統的な方法を使用する必要があり、一定の制限を持っている必要があります。変化にPreBlockSpin:デフォルト値は、ユーザがパラメータ-XXを使用することができ、スピンの10倍の数です。


2.4適応スピンロック

JDK1.6でも適応スピンロックを導入しました。適応手段は、スピン時間はもはや固定されないが、スピンロック時間た状態でロックされ、所有者が以前によって決定されます。同じオブジェクトスピンウェイトロックを保持しているだけで、正常優勝ロック、およびスレッドのロックが実行されている場合、仮想マシンは、これは非常に高い成功したスピンは再びなることもあると思いますし、それがスピンを許可します100サイクルとして、時間の比較的長い期間などを待ちます。また、もしスピンめったに成功していない、と彼らが欲しがっているのロック
ロックは、プロセッサリソースの浪費を避けるために、スピン工程を省略することが可能になるとき。適応スピンでは、プログラムが実行され、継続的に情報を監視し、パフォーマンスを向上させ、仮想マシンプログラムのロック状態予測がより正確になり、仮想マシンは、より多くの「スマート」になります。


3 JDK1.6他のsynchronizedキーワードの最適化入門


3.1ロックの排除

ロック除去は、実行時に仮想マシンタイムコンパイラ(JIT)、同期のためのいくつかのコードの要件はあるが、競争を排除するためにラッチされ、共有データを検出することはできません。ロックがメイン判決ベースから排除逃逸分析におけるコードの一部は、ヒープが他のスレッドにアクセスできるように脱出しませんかどうかを判断するために、すべてのデータをサポートするためのデータ、それはスタック上のデータとして扱うことが可能であり、それらが考慮されますプライベートスレッド、同期を必要とせずに自然にロックされました。それは、データが分析を決定するために流れて仮想マシンを使用する必要がありますが、プログラマは非常に明確であるべきための変数は、エスケープされ、また、データの競合、それの同期を必要と明確なケースが存在しないことを知っているだろうか?実際に多くあります。対策は、Javaプログラムにおける同期コードの有病率は、ほとんどの読者の想像力を超えることがあり、同期プログラマは追加されません。

たとえば、唯一の3つの出力プログラムソースの文字通りまたはセマンティクスが同期されていないかどうかを加算結果、この非常に単純なコード列に続きます。

public class Demo01 {
    
    public static void main(String[] args) {
        contactString("aa", "bb", "cc");
    }
    public static String contactString(String s1, String s2, String s3) {
        return new StringBuffer().append(s1).append(s2).append(s3).toString();
    }
}

StringBufferのはappend()は、同期方法であって、これはロック(新規のStringBuilder())であることです。その動的仮想マシンの検出スコープはconcatString()メソッド内に閉じ込められます。つまり、オブジェクトはconcatString()メソッド、他のスレッドはロックがそうしながら、それにアクセスすることはできませんが、安全に除去することができ、インタイムコンパイルに「エスケープ」になることはありません)(新規のStringBuilderを引用、と言うことですその後、このコードは直接実行されたすべての同期を無視します。


3.2 锁粗化

原則的に、我々は、コードの作成には常に可能な限り小さくシンクブロックの範囲を限定することをお勧めしますが、唯一の同期のために、そうできるようにするために、操作の数は、共有データの実際の範囲が小さい同期させるために必要ですロック競合がある場合は、スレッドができるだけ早くロックを得ることができることを、ロックを待っています。

ほとんどの場合、上記の原理は正しいが、連続した一連の動作は、しばしば、同じオブジェクトのロックとロック解除を繰り返し、あるいはロック動作がループ本体に存在し、どのスレッドの競合が存在しない場合でも、そのされている場合ミューテックス同期動作も不要パフォーマンスの低下を引き起こします。次のコード例:

class Demo02 {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        //StringBuffer是同步方法,
        // 其实没必要每次append都去判断锁相关的内容,可以将整个for循环搞成同步的 ---> JVM的锁粗化可能会直接帮你这样弄
        for (int i = 0; i < 100; i++) {
            sb.append("aa");
        }
        System.out.println(sb.toString());
    }
}

ロック粗大化とは何ですが、私は次のように定義されて与える、あなたが理解しなければならないと考えています。

JVMは、同じロックオブジェクトを使用小さな一連の動作を検出し、同期コードブロックの範囲は、ロック一度だけ追加することができるように、動作中にこの文字列の外側に、拡大しました。


終わり

公開された226元の記事 ウォンの賞賛319 ビュー530 000 +

おすすめ

転載: blog.csdn.net/nrsc272420199/article/details/105232637