原則同期ロック機構
シンクロナイズドJavaは、同期を達成するためのロック機構を使用して同期基礎となる同期のためのキーワードのようなものです。Javaで各オブジェクトには、ロックとして使用することができます。
Javaは二つの特徴で同期:
-
相互に排他的な:つまり、同時に単一の郡を可能にする唯一のスレッド同期符号が同時にアクセスされるように、この特性の複数のスレッドを達成するために、調整メカニズムをオブジェクトのロックを保持しています、独占は、多くの場合、アトミックと呼ばれます。
-
可視性:あなたは、ロックを取得する際に共通の価値観と同じメモリの主な変数のスレッドをことを確認する必要があり、また、放出される前に、ロックで共有変数に変更を加え、その後、別のスレッドのロックを取得していることを確認する必要があります(すなわち、共有変数の最新の値から得られなければならないロックを取得する場合)、そうでなければ、別のスレッドが一貫性のない結果をもたらすローカルにキャッシュされたコピー上で動作し続けることができる見えます。
同期ロック特定の三つの形式:
- 通常の同期方法のために、ロックオブジェクトは、オブジェクトの現在のインスタンスであり、現在は同期ブロックに入る前に、ロックオブジェクトを取得するために必要。
- 静的な同期メソッドの場合、オブジェクトは、すべてのクラスがClassオブジェクトを持ってJavaで、現在のクラスをロックすることです。
- 同期方法のブロックについて、同期括弧内のオブジェクトをロックするクラスオブジェクトは、その後、いわゆるクラスロックである場合クラスロックであるが、ここでの目的は、通常のオブジェクトであってもよいし、クラスオブジェクトであってもよいですクラスオブジェクトクラスの実装によって。
同期の原則
パブリック クラスSynchronizedTest { 公共 のボイドreadFileの()はIOExceptionをスローします{ 同期(本){ 。システムOUTの .println(" シンクブロック" ); } } }
javapのデコンパイルした後、
C:\データ>てjavap -c SynchronizedTest。クラス コンパイルから 「SynchronizedTest.java 」 パブリック クラスtest.test.a.SynchronizedTest { パブリックtest.test.a.SynchronizedTest()。 コード: 0 :aload_0 1:invokespecial#8 // 。メソッドのJava /ラング/オブジェクト"<初期化>" :()V 4:リターン 公共 のボイドreadFileの()にjava.io.IOExceptionがスローされます。 コード: 0 :aload_0 1 DUP: 2 :astore_1 3 :monitorenter 4:getstatic#18 // フィールドのJava / LANG /のSystem.out:Ljava / IO /のPrintStream。 7:LDC#24 // 文字列同步代码块 9:INVOKEVIRTUAL#26 // メソッドJAVA / IO / PrintStream.println:(Ljava /ラング/文字列;)V 12 :aload_1 13 monitorexit: 14:後藤 20 17 :aload_1 18 :monitorexit 19 :athrow 20:リターン 例外テーブル: ターゲット・タイプに 4 14 17 の任意の 17 19 17 任意 }
これは、同期コードブロックの終了位置を示す同期monitorenterとmonitorexit命令を使用して文のシンクブロック、ブロックの同期コードポイントの前記monitorenter命令開始位置、monitorexit命令を理解されよう。
以下は、同期方法であります:
パブリック クラスSynchronizedTest { 公共同期無効readFileのは、(){IOExceptionがスローされます 。システムOUTの .println(" シンクブロック" ); } }
下記に示す逆コンパイル:
これらは、異なるオブジェクトヘッダは、次の表にまとめます:
長さ | コンテンツ | 説明 |
---|---|---|
32/64ビット | マーク・ワード | hashCodeストレージオブジェクト、GCとロック情報世代 |
32/64ビット | クラースポイント | クラスのメタデータへのポインタを格納 |
32/64ビット | 配列の長さ | これは、オブジェクトのみのアレイのためにストレージアレイの長さであります |
MarkWordのJavaは、以下の表にオブジェクトヘッダ内に格納されているオブジェクト:
ロック状態 | 25bit | 4ビット | バイアスロッキングか1ビット | 2ビットのロックフラグ |
---|---|---|---|---|
ロックフリー状態 | hashCodeオブジェクト | オブジェクトの世代年齢 | 0 | 01 |
動作中に、マーク・ワードに格納されたデータは、ロックフラグによって異なります、それは以下の表を変更します。
次のようにストレージ構造64ビット仮想マークワード64ビットのストレージ構造、で:
ロック状態 | 25bit | 31bit | 1ビット | 4ビット | 1ビット | 2ビット |
---|---|---|---|---|---|---|
cms_free | 世代別年齢 | バイアスされたロック | ロックフラグ | |||
いいえロックません | 未使用 | ハッシュコード | 0 | 01 | ||
バイアスされたロック | スレッドID(54bit) | エポック(2ビット) | 1 | 01 |
ロックフラグ01の先頭に格納された最後の2つのオブジェクトが初期状態にないロックがないことを意味し、それはロックのさまざまなレベルで、頭部に格納されたコンテンツオブジェクトは異なるであろう、あなたのために予めハッシュオブジェクトに格納されたオブジェクトです。バイアスは、現在のオブジェクトを保持して格納されているスレッドIDをロックすると、軽量メモリロックは、スレッド・スタック・ポインタを向けロック・レコードです。
モニタモニタのロック
其中轻量级锁和偏向锁是Java6对synchronized锁进行优化后增加的,我们稍后会进行分析。这里我们主要分析重量级锁,也就是通常所说的synchronized对象锁,锁标识为10,其中指针指向monitor对象(也称之为管程或者监视器锁)的起始地址。每个对象都存在一个monitor与之关联,对象与其monitor之间也存在着多种实现方式,如monitor可以与对象一起创建或者销毁或当前线程试图获取锁时自动生成,但一个monitor被某线程持有后,它便处于锁定状态。在Java虚拟机(HotSpot)中,monitor是有ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)
ObjectMonitor() { _header = NULL; _count = 0; //记录个数 _waiters = 0, _recursions = 0; _object = NULL; _owner = NULL; _WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet _WaitSetLock = 0 ; _Responsible = NULL ; _succ = NULL ; _cxq = NULL ; FreeNext = NULL ; _EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表 _SpinFreq = 0 ; _SpinClock = 0 ; OwnerIsThread = 0 ; }
ObjectMonitor 中有两个队列,_WaitSet 和 _EntryList ,用来保存ObjectWaiter 对象列表,每个等待锁的线程都会被封装成ObjectWaiter 对象,_owner 指向持有ObjectMonitor 对象的线程,当多个线程同时访问同一同步代码块或者同步方法时,首先会进入 _EntryList 队列,当线程获取到monitor 后进入_Owner 区域并把 monitor中的 _Owner 变量设置为当前线程,同时monitor 中的计数器count 加1,若线程调用wait() 方法,将释放当前持有的monitor,_owner变量恢复为null,count 自减 1 ,同时该线程进入_WaitSet 集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。如下图所示:
所以,monitor对象存在于每一个java对象的对象头(存储指针的指向),synchronized锁便是通过这种方式获取的,也是为什么java中任何对象都可以作为锁的原因,同时也是 notify/notifyAll/wait 方法等存在于顶级对象Object中的原因。
原文链接:https://www.jianshu.com/p/3eda3d375e7e