原則として、同期ロック機構

原則同期ロック機構

シンクロナイズドJavaは、同期を達成するためのロック機構を使用して同期基礎となる同期のためのキーワードのようなものです。Javaで各オブジェクトには、ロックとして使用することができます。

Javaは二つの特徴で同期:

  1. 相互に排他的な:つまり、同時に単一の郡を可能にする唯一のスレッド同期符号が同時にアクセスされるように、この特性の複数のスレッドを達成するために、調整メカニズムをオブジェクトのロックを保持しています、独占は、多くの場合、アトミックと呼ばれます。

  2. 可視性:あなたは、ロックを取得する際に共通の価値観と同じメモリの主な変数のスレッドをことを確認する必要があり、また、放出される前に、ロックで共有変数に変更を加え、その後、別のスレッドのロックを取得していることを確認する必要があります(すなわち、共有変数の最新の値から得られなければならないロックを取得する場合)、そうでなければ、別のスレッドが一貫性のない結果をもたらすローカルにキャッシュされたコピー上で動作し続けることができる見えます。

同期ロック特定の三つの形式:

  1. 通常の同期方法のために、ロックオブジェクトは、オブジェクトの現在のインスタンスであり、現在は同期ブロックに入る前に、ロックオブジェクトを取得するために必要。
  2. 静的な同期メソッドの場合、オブジェクトは、すべてのクラスがClassオブジェクトを持ってJavaで、現在のクラスをロックすることです。
  3. 同期方法のブロックについて、同期括弧内のオブジェクトをロックするクラスオブジェクトは、その後、いわゆるクラスロックである場合クラスロックであるが、ここでの目的は、通常のオブジェクトであってもよいし、クラスオブジェクトであってもよいですクラスオブジェクトクラスの実装によって。

同期の原則
 
リリースJVMをベースにし、入力モニタは、メソッドと同期コード・ブロック、2つの異なる実装の詳細を実装するオブジェクト。
 
シンクブロック:使用monitorenterとmonitorexit命令。
修正された方法は、同期monitorenterとmonitorexit命令、及び置換がACC_SYNCHRONIZEDされた識別フラグは、対応する同期呼び出しを実行するように、この方法は、同期方式であることを示していません。
 
Monitorenter JVMは、各々がそれに対応するmonitorterのmonitorexitを有していなければならないことを保証するために、命令は、同期コードブロックの開始位置を達成コンパイル、及び異常挿入されmonitorexit方法は終了します。任意のオブジェクトをモニターに関連付けることができ、かつコマンドがmonitorenterを実行したときに、カッコ内の所有権は、それはヘビー級のロックで、ここで(オブジェクトのロックを取得しようと試みるに関連付けられているオブジェクトを同期モニター取得しようとします、)ロックロックと軽量は後述するバイアスされていません
 
具体的な例で見てみましょう:
パブリック クラス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(" シンクブロック" );
    }
}

下記に示す逆コンパイル:

 

 

 
Javaオブジェクトヘッド
 
鍋、仮想マシンは、オブジェクトヘッダは、2件の主要なデータが含まれています。
 
マーク・ワード(タグフィールド)
クラースポイント(ポインタの種類)
 
MarkWord:デフォルトのストレージ・オブジェクトのhashCode、GCと世代ロックフラグ情報。マークワードが非固定データ構造であるように設計されているので、データ自体をオブジェクト定義から独立している情報なので、多くのデータが小さな空間に格納されています。(マークワード仮想マシン内の32バイトのためのサイズ32個のアカウントは、64ビットの仮想マシンで64バイトを占めます)。そのデータは、動作中、ロックフラグに応じて変化するマークワード内に格納されています。
クラースポイント:そのクラス・オブジェクトへのポインタは、メタデータであり、仮想マシンがこのオブジェクトのポインタによって決定され、そのクラスのインスタンスです。これは、64ビットの仮想マシンで64バイトを占め、32ビット仮想マシンの32バイトの同じサイズを占めています。
 

これらは、異なるオブジェクトヘッダは、次の表にまとめます:

長さ コンテンツ 説明
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

おすすめ

転載: www.cnblogs.com/yrjns/p/12152691.html