ロックのJavaの原則

同期の実装原理

ここに画像を挿入説明

同期コードを達成するためにボトムブロック

コードの一部を見てください:

class Test{
    public static void main(String[] args) {
      Object obj=new Object();
      synchronized(obj){
          System.out.println("hello world");
      }
    }
}

彼の基本的な実装を見て、議会ターン:あなたの作成したクラスファイルを右クリック- > terminal->入力アセンブリ命令のJavaクラス名.javaファイルで開い見つける- >てjavap -vクラス名が
見える:
ここに画像を挿入説明
概要:
同期ブロックを実行ときmonitorexit命令の実行の最初の命令はmonitorenter終了を実行する必要があります後。キーがある、同期同期用いた分析の後に見ることができ、監視オブジェクトの取得を監視するために必要なスレッドがそうでなければ、それは待つことができ、ダウン継続してモニターを取得し、。このプロセスの買収は、一つだけのスレッドがモニターに得ることができること**、あること、相互に排他的です。** Java仮想マシンが取得したロックは、通常の実行パスでロックを解除することができることを保証するだけでなく、異常な実行パスする必要があるため、我々は、より多くのmonitorexitのmonitorenter命令や指示を発見した上記のアセンブリ命令を守ってください。

基本となる同期の実装の方法

例:

class Test{
    public static void main(String[] args) {
        test();
    }
      public static synchronized void test(){
            System.out.println("hello world");
        }
}

コンパイルが含まれています:
ここに画像を挿入説明

同期標識法を使用する場合は、バイトコードACC_SYNCHRONIZEDアクセスタグが表示されます。フラグは、プロセスに入る、JVMのmonitorenter必要な動作を示しています。メソッドを出る際にかかわらず、正常復帰するかどうかの、JVMは必要monitorexit動作です。ここでmonitorenterと操作に対応するmonitorexitロックオブジェクトが暗黙的です。インスタンスメソッドのために、二つの操作に対応するオブジェクトのロックは、このされ、静的メソッドのために、これら二つの例の動作に対応するオブジェクトのロックは、ここでクラスのクラスです。

moniterメカニズム

ASとmonitorenterのmonitorexitの役割に、私たちは抽象的に理解することができ、各ロックオブジェクトは、ロックカウンタとロックを保持しているスレッドへのポインタを持っています。
monitorenterを行う場合、ターゲット・オブジェクトのロック・カウンタがゼロを、ある場合には、別のスレッドによって保持されていないこと。この場合、JVMは、現在のスレッドのロックオブジェクトを保持しているスレッドを設定し、そのカウントがインクリメントされます。
カウンタロックの場合には、ターゲットオブジェクトがゼロでないスレッドロックオブジェクトの所有者は、現在のスレッドその場合、JVMは(再び+1に対抗することができるリエントラントロック)、そうでない場合は、保持スレッドがロックを解放するまで待つ必要があります。
monitorexitを行う際には、Java仮想マシンは、1つデクリメントされたオブジェクトのカウンタをロックするために必要とされます。カウンタがゼロになると、それはロックアウトに代わってリリースされていたであろう。
また達成ヘビーJDK1.6として知らJDK1.6同期機構根底にある原理の前にオブジェクトロック(モニタ)、カーネルモードの切り替えにスレッドブロッキングおよびウェイクアップ・オペレーティング・システムのユーザモードは、オーバーヘッドが非常に大きいので、非効率的です。

リエントラントロックの説明

誰もが物語を与える - 村の内部には、井戸の水は、水の品質は非常に良いですがあり、村人は水でよく掘るしたいです。これはよく、非常に多くの人々の村は、その仕事のルールと引き分けに来るだけです。最終的には市長の脳、とは、井戸が秩序を維持するために水をフェッチ、よく見るために人を配置する合理的な計画を思い付きました。よく家族これ誰に最初に水、家族単位をフェッチするときは、水を打つことができますが、家族がキック権利を占めた場合にラインアップしていないこの時点で彼の家族を蹴ってきます。水を描画する権利をつかむしなかった人、一つ一つは、先着のほか、上面の隣に並びました。再入可能ロックと何それは問題ではないと言いましたか?私たちは、次のシナリオを見て、心配しないでください。あなたはAが水をフェッチするために十分に最初のスレッドであると言うならば、彼は、スレッドB、C、Dの後に来た...と、これだけ次々と来る、規則に従って、もちろん、スレッドAのヒット水(状態1)、他スレッドが待っているが、それでも水汲みに糸を通すとき、息子、それを行うために並んで待っているスレッドの息子を通すために?もちろんない、これはデッドロックしますので。ロック要求が再び、同じ家族の中に水をフェッチすることと等価であるときにも水を汲みに来たので、それは(ロック状態を再度1)特権です。ロックが解除されるように糸ロックは、状態0までそれぞれ、-1ロック状態一旦解除されます。競争再び次のスレッドロック。

設けられたロックロック

例:使用ReentrantLockの同期

class MyThread implements Runnable {
    private int ticket = 10;
    private Lock ticketLock = new ReentrantLock();
    public void run() {
        for (int i = 0; i <= this.ticket; i++) {
            ticketLock.lock();
            try {
                if (this.ticket > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ",还有" + this.ticket-- + " 张票");
                }
            } finally {
                ticketLock.unlock();
            }
        }
    }
}
class Test{
    public static void main(String[] args) {
      MyThread myThread=new MyThread();
      new Thread(myThread,"A").start();
     new Thread(myThread,"B").start();
     new Thread(myThread,"C").start();
    }
}

結果:
ここに画像を挿入説明
JDK1.5での同期非効率的なパフォーマンス、インチ これはヘビー級操作であるため、その最も大きな影響は、性能が達成され、ブロッキングされ、カーネルモード動作は、並行処理の多くをもたらすには、完了するためにこれらのオペレーティングシステムが必要にスレッドのスレッドを中断し、再開圧力。これとは対照的にJavaを使用してロックオブジェクトは、より高いパフォーマンスを提供します。

同期の最適化

CAS操作
CASは何ですか?

ロックを使用する場合、スレッドはロックが取得悲観的ロック現在のスレッドもロックを取得するために他のスレッドをブロックするロック時間を取得するようにコード(共有リソースへのアクセス)のクリティカルセクションの各実行は、競合を持つことを前提と戦略。
CAS動作は(また、ロックフリー操作としても知られる)楽観的ロック戦略である、全てのスレッドが共有リソースにアクセスすることを仮定した場合、競合が天然に存在しないので、競合は、他のスレッドの動作を妨げないありません。したがって、スレッドは休止状態が発生したブロックしません。衝突があったのであれば、どのように行うには?いいえロック操作が競合があるかどうかを識別するために、スレッド比較交換として知られるCASを(比較およびスワップ)を使用することなく、競合は競合が存在しなくなるまで、現在の操作を再試行するように思われました。

CASの動作中

CASは、CAS(V、O、N)として理解することができる:
V:実際の格納された値の現在のメモリアドレス
O:期待値(古い値)
N:新しい更新された値
** VおよびOは、古い値を言うことである、同じである場合そして、同じメモリの実際の値が値である前に、別のスレッドによって変更されていないことを示し、古い値Oは現在、最新の値のためのものであり、自然に新しい値がN Vに割り当てることができます 逆に、VおよびOは、あなたが新しい値V Nは、Vを返すことができます割り当てることはできませんので、値の最新バージョンではない値がOの古い値を裏返しにする別のスレッドとなっていることを示し、同じではありません。複数のスレッドがCAS操作する変数を使用する場合は**、一つのスレッドだけが成功すると、成功した更新、残りは失敗します。失敗)が再び糸を回転、またはハングスレッド(ブロック)を選択します。
注:仮想マシンは、プロセッサの実装によって提供されるJDK1.5 CMPXCHG命令を使用する後CAS実装は、ハードウェアサポートされる命令セットを必要とします。
内蔵ロック(同期)古いバージョンでの最大の問題の嘘:スレッドがブロックしているとウェイクによる性能の問題は(同期をブロックする)相互排除問題である競争のスレッドの存在下で起こります。CASは、いくつかの操作を行うための試みに失敗した後にCASは、スレッド懸濁液に関するものではない時間がかかる(非ブロッキング同期とも呼ばれる)スレッド懸濁液にありません

CASの問題
  • ABAの問題は、
    古い値をチェックしますCASは変更されていないので、ここで興味深い質問があります。例えば、BにAへの古い値は、次にAに、単に古い値を行い、CAS検査は何の変化がまだAではない明らかにしたが、実際には変更をしました。ソリューション**バージョン番号を解決することができます追加、共通データベース楽観的ロックモードに従うことができます。CASは、古い値が変更されていないチェックしますので、**は、ここで興味深い質問があり、過去にJDK1.5後、原子のパッケージにAtomicStampedReferenceを提供します。例えば、BにAへの古い値は、次にAに、単に古い値を行い、CAS検査は何の変化がまだAではない明らかにしたが、実際には変更をしました。ソリューションは、共通のデータベース楽観的ロックの道をたどることができ、バージョン番号を解決することができます追加します。AtomicStampedReferenceは、ABAの問題を解決するために、JDK1.5の後、原子のパッケージで提供します。
    2. スピンは、プロセッサリソースを大量無駄に
    糸と比較がブロックされ、スピンはプロセッサリソースを浪費することになります。現在のスレッドが条件を実行しているにまだあるので、これはですが、ランニングは無用命令です。これは、ロックが解除することができ、無用命令の動作中に望ましいです。JVMプログラムが与えられ
    、適応スピン
    スピン時間(サイクル)デバッグ動的にロックするために、従来のスピン待ち時間に従って得られたかどうか、。あなたがロックをスピンするために取得した場合、それは少しわずかに減少される次のスピン、スピンや長い次回の長さが増加します。
    3. 公平性の
    スピン状態は、他の副作用、不公平なロック機構をもたらしました。スレッドは、すぐに解放されるロックを競うことができないブロックされています。しかし、スレッドのスピン状態では、ロックに優先的にアクセス可能性があります。だから内蔵の公平を実現することができないロック機構、公平かつロックシステムをロックすることができます。
Javaオブジェクトヘッダ

ローからハイへの四つの状態のJDK1.6ロックレベルの総後ない:
なしロック001
101ロック付勢
軽量ロック00
ヘビーロック10を
、ロック状態における競争の強度に応じて自動的にアップグレードされ、ロックができませんダウングレード(効率を改善した放出を得るために)

バイアスされたロック

1.バイアスされ、ロックの起源:

   大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得,为了让线程获取锁的开销降低引入偏向锁

偏ったがロック状態をロックすることは最も楽観的ロックの一つである:開始から終了要求ロックに1つのスレッドだけ。
2.へのアクセスをロックバイアス:

   - 测试对象头Mark Word(默认存储对象的HashCode,分代年龄,锁标记位)里是否存储着指向当前线程的偏向锁。
   - 若测试失败,则测试Mark Word中偏向锁标识是否设置成1(表示当前为偏向锁)
   - 没有设置则使用CAS竞争,否则尝试使用CAS将对象头的偏向锁指向当前线程

3.バイアスロック失効:大きなオーバヘッド

- 偏向锁使用了一种等待竞争出现才会释放锁的机制,其他线程竞争偏向锁
- 暂停拥有偏向锁的线程,检查线程是否存活
- 处于非活动状态(即终止状态),则将锁对象的对象头设置成为无锁状态
- 存活,则重新偏向于其他线程或者恢复到无锁状态或者释放偏向锁并将锁膨胀为轻量级锁
- 唤醒线程

ここに画像を挿入説明
JDK6がバイアスされた後、ロックはデフォルトで有効になって。しかし、唯一のアプリケーションは、数秒を開始した後、必要に応じて、JVMをシャットダウンするために使用することができディレイパラメータアクティブ化されています。「 - XXは:BiasedLockingStartupDelay = 0 」。あなたは、通常の状況下での競争状態にあるすべてのロックでアプリケーションを決定した場合、あなたがすることができ
-XX:偏ったロックJVMパラメータ閉じ-UseBiasedLocking =偽の場合、プログラムはデフォルト軽量ロック状態になります。

軽量ロック

複数のスレッドが異なる期間で同じロックを要求しないロック競合が存在しないことを意味します。
1.ロック

  如果成功使用CAS将对象头中的Mark Word替换为指向锁记录的指针,则获得锁,失败则表示其他线程竞争锁,当前线程尝试使用自旋(循环等待)来获取锁。

2.ロック解除

 轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。为了防止继续自旋,一旦升级,将无法降级。

ここに画像を挿入説明

ヘビーロック

ヘビー級のロック機能します:

別のスレッドがブロックされますロックを取得しようとすると、スレッドが唯一の競争するためにロックを保持しているロックを解放した後、それがスレッドを起動します。

三種類の機能をロックします:

まずは、三つの小さな例で3つのロックを区別する説明しましょう:

  假如家里只有一个碗,当我自己在家时,没有人会和我争碗,这时即为偏向锁状态
  
  当我和我妹都在家吃饭时,如果我妹不是很饿,则她会等我吃完再用我的碗去吃饭,这就是轻量级锁状态

  当我和我妹都很饿的时候,这时候就会去争抢这唯一的一个碗吃饭,这就是重量级锁状态
  1. ヘビー級のロックは、要求ウェイクロックしているスレッドをブロックします。これは、同じケースロックを競合する複数のスレッドを対象としています。JVMマイニングはによって起こされ、それがブロックされるコードの非常に小さな同期ブロック、の顔にスレッドを避けるためにスピンする必要があります。
  2. 元のタグフィールドが格納されているように、ロックオブジェクトのタグフィールドを置き換えるためにCAS操作を使用して軽量ロックは、現在のスレッドのスタック上の空間へのポインタです。異なる期間における複数のスレッドが同じロックを適用するためのケースです。
  3. 最初の要求にCASを使用するだけロックバイアスされ、現在のスレッドのアドレスは、オブジェクトのロックのタグフィールドに記録されます。動作時にバイアスされ、ロックねじロック操作の保持の後に直接戻ります。それだけで同じスレッドで開催されるロックの場合です。
锁粗化

ロック粗大化を一つに一緒に複数のロック、アンロック操作を接続することで、連続した拡張ロック複数のより大きな範囲ロックになること。
例:

public class Test{
  private static StringBuffer sb = new StringBuffer();
  public static void main(String[] args) {
      sb.append("a");
      sb.append("b");
      sb.append("c");
   }
}

仮想マシンが同じオブジェクトのロックとロック解除の操作の一連の系列を検出した場合、ここでstringBuffer.append方法がロックおよびロック解除必要とするすべての呼び出しは、ロック・アンロックの広い範囲にそれらをマージします最後appendメソッドの終了後にロックを解除する最初の追記方法でロックされている操作、。

public class test {
    private static StringBuilder sb=new StringBuilder();//线程不安全
    public static void main(String[] args){
      synchronized (sb){
          sb.append("a");
          sb.append("b");
          sb.append("c");
      }
    }
}
ロック排除

ロックは不要なロック操作を排除するために削除されます。コードの回避技術によると、ヒープデータのコードの一部は、現在のスレッドをエスケープしていないかどうかを判断するために、あなたは、このコードはスレッドセーフ、不要なロックであると考えることができます。

public class Test{
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        sb.append("a").append("b").append("c");
    }
}

StringBufferの者は、ローカル変数に属し同期方法が、このプログラムのStringBufferを追加し、そのため実際には、プロセスから逃れられないだろうが、このプロセスは、スレッドセーフなロックをなくすことができることです。

例:ロックの排除

public class test {
    public static void main(String[] args){
      StringBuilder sb=new StringBuilder();
      sb.append("a").append("b").append("c");
    }
}

公開された87元の記事 ウォン称賛73 ビュー10000 +

おすすめ

転載: blog.csdn.net/HL_HLHL/article/details/84619804