前の同期の実装原理 moniterに言及、それを導入していませんでした。
同期方法や同期ブロックするかどうか、それがあるかどうACC_SYNCHRONIZED
かmonitorenter
、monitorexit
に基づいていますMonitor
実現し、これを紹介するものですモニター。
オペレーティングシステムの管
あなたは、オペレーティングシステムの大学で勉強している場合は、チューブ(モニター)は、オペレーティングシステムの非常に重要な概念であることを覚えていることがあります。同様にも使用されているJava同期メカニズムで監視します。
チューブ(英語:もモニターと呼ぶモニター、)プログラム構造、共有リソースへの相互排他的アクセスによって形成された構造内のサブプログラムの複数のワーカースレッド(モジュール又はオブジェクト)の複数です。これらの共有リソースは、一般的に、ハードウェアデバイスまたは変数のグループです。チューブは、ある時点でチューブのサブルーチンを実行せいぜい一つだけスレッドを実現します。構造設計を変更することによって、データへの相互排他的なアクセスを実現並行プログラムに比べ、大幅に達成するチューブは、プログラミングを簡素化します。チューブは、スレッドは一時的に排他的アクセスを回復する権利の実現を取り戻すために、一定の条件が満たされるのを待ち、排他的アクセスを放棄することができ、メカニズムを提供します。
Javaスレッドの同期Moniter関連
複数のスレッドが共有リソースにアクセスすると、多くの場合、可視性とアトミック安全性の問題をもたらします。スレッドの安全性の問題のこの種を解決するために、Javaは同期メカニズム、リソースは同時に1つのスレッドのみ共有にアクセスできることが保証さミューテックスのロック機構を提供します。監視ロックモニタからの保護のこのメカニズムは、各オブジェクトは、独自の監視ロックモニタを持っています。
まず、我々のソースコードでは、例を与えます。私たちは、建物の特別な部屋、この特定の部屋同時に、1つのゲストのみ(スレッド)を含むものと理解監視することができます。部屋には、データとコードの数が含まれています。
顧客は、この特定の部屋を入力したい場合は、彼が最初に廊下(エントリーセット)に並んで待つ必要があります。スケジューラは、クライアントを選択するためのキューが部屋に入る(例えばFIFOなど)一定の基準に基づいて行われます。もし、何らかの理由で、一時的にため、彼らは逃れることはできない他のもののクライアントのクライアント(スレッドが中断されている)ため、その後、彼は(設定を待って)待つために特別な部屋に加えて送信されます、この部屋は少しすることができ後に、その特定の部屋に再び入ります。前述したように、建築家3ヶ所の合計。
要するに、モニターは、スレッドは、特別な部屋に入る監視するために使用されます。彼の任務は、(同時に)一つだけのスレッドが保護されたデータやコードにアクセスできるようにすることです。
同期ツールの種類も同期メカニズムであると言うことができるモニターが、それは多くの場合、ターゲットとして記述されている、主な機能は次のとおりです。
オブジェクトのすべてのメソッドは、実行の「相互に排他的」です。スレッドが返されたライセンスを残すために、この「許可」を得るために必要なメソッドのいずれかを入力すると、「ライセンス」のように一つだけの実行を監視します。
通常、葛のメカニズムを提供します。一時的に真述語(条件変数)を待って、スレッドの「許可」を放棄する正のホールド「許可」を可能にし、条件が満たされている、現在のプロセスは、スレッドが条件変数を待っている「知らせる」ことがあり、彼は許可を得るために、再度実行することができるように。
モニタを実現
MonitorがでC ++の実装に基づいており、中にJava仮想マシン(ホットスポット)でObjectMonitorその主要なデータ構造を実現する以下のとおりです。
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
ObjectMonitorいくつかの重要な属性があります。
_owner:オブジェクトのスレッドを指してはObjectMonitorを保持しています
_WaitSet:待ち状態にある店舗のスレッドキュー
_EntryList:ブロックをロックするスレッドの待機キューに格納されています
_recursions:再入国ロックの数
_count:回数にロックを獲得するためのスレッドを記録するために使用
複数のスレッドアクセス同期コードセクションが最初に入るときに_EntryList
キューを、オブジェクトがモニタに入るときに得られたスレッド_Owner
領域および監視するために、_owner
変数が現在のスレッドに設定され、カウンタが監視されて_count
インクリメントされます。つまり、オブジェクトのロックを取得します。
保持監視スレッドが呼び出す場合wait()
の方法を、それが現在開催されたモニターを解放する、_owner
変数回復があるnull
、_count
にスレッドながら、マイナス1から_WaitSet
コレクションが起こされるのを待っています。現在のスレッドが終了すると、他のスレッドが(ロック)モニターに入るように、モニターを解除する(ロック)と、変数の値をリセットします。下図のように:
ObjectMonitorクラスには、いくつかのメソッドを提供します。
ロックを取得します。
void ATTR ObjectMonitor::enter(TRAPS) {
Thread * const Self = THREAD ;
void * cur ;
//通过CAS尝试把monitor的`_owner`字段设置为当前线程
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
//获取锁失败
if (cur == NULL) { assert (_recursions == 0 , "invariant") ;
assert (_owner == Self, "invariant") ;
// CONSIDER: set or assert OwnerIsThread == 1
return ;
}
// 如果旧值和当前线程一样,说明当前线程已经持有锁,此次为重入,_recursions自增,并获得锁。
if (cur == Self) {
// TODO-FIXME: check for integer overflow! BUGID 6557169.
_recursions ++ ;
return ;
}
// 如果当前线程是第一次进入该monitor,设置_recursions为1,_owner为当前线程
if (Self->is_lock_owned ((address)cur)) {
assert (_recursions == 0, "internal state error");
_recursions = 1 ;
// Commute owner from a thread-specific on-stack BasicLockObject address to
// a full-fledged "Thread *".
_owner = Self ;
OwnerIsThread = 1 ;
return ;
}
// 省略部分代码。
// 通过自旋执行ObjectMonitor::EnterI方法等待锁的释放
for (;;) {
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition()
// or java_suspend_self()
EnterI (THREAD) ;
if (!ExitSuspendEquivalent(jt)) break ;
//
// We have acquired the contended monitor, but while we were
// waiting another thread suspended us. We don't want to enter
// the monitor while suspended because that would surprise the
// thread that suspended us.
//
_recursions = 0 ;
_succ = NULL ;
exit (Self) ;
jt->java_suspend_self();
}
}
ロックを解除
void ATTR ObjectMonitor::exit(TRAPS) {
Thread * Self = THREAD ;
//如果当前线程不是Monitor的所有者
if (THREAD != _owner) {
if (THREAD->is_lock_owned((address) _owner)) { //
// Transmute _owner from a BasicLock pointer to a Thread address.
// We don't need to hold _mutex for this transition.
// Non-null to Non-null is safe as long as all readers can
// tolerate either flavor.
assert (_recursions == 0, "invariant") ;
_owner = THREAD ;
_recursions = 0 ;
OwnerIsThread = 1 ;
} else {
// NOTE: we need to handle unbalanced monitor enter/exit
// in native code by throwing an exception.
// TODO: Throw an IllegalMonitorStateException ?
TEVENT (Exit - Throw IMSX) ;
assert(false, "Non-balanced monitor enter/exit!");
if (false) {
THROW(vmSymbols::java_lang_IllegalMonitorStateException());
}
return;
}
}
// 如果_recursions次数不为0.自减
if (_recursions != 0) {
_recursions--; // this is simple recursive enter
TEVENT (Inflated exit - recursive) ;
return ;
}
//省略部分代码,根据不同的策略(由QMode指定),从cxq或EntryList中获取头节点,通过ObjectMonitor::ExitEpilog方法唤醒该节点封装的线程,唤醒操作最终由unpark完成。
また方法、出入りするobjectMonitor.cppそこを
void wait(jlong millis, bool interruptable, TRAPS);
void notify(TRAPS);
void notifyAll(TRAPS);
そして、他の方法。
概要
原則のロックとアンロックのHotSpot仮想マシンMoniterは、上記で説明しました。
この記事を通して、私たちは知っているsychronized
呼び出しobjectMonitorロックenter
メソッドが呼び出されたロックを解除するexit
方法を。実際には、唯一のJDK1.6前に、synchronized
実装は直接ObjectMonitorを呼び出しますenter
し、exit
このロックはヘビー級のロックと呼ばれています。なぜ、このようなやり方は重いそれをロック?
Javaスレッドは、オペレーティングシステムのネイティブスレッドの一番上にマッピングされているか、ブロックされたスレッドをウェイクアップしたい場合はカーネルモードために、ユーザーモードから変換されたオペレーティングシステム、の助けが必要になりますので、状態遷移は、プロセッサ時間がかかります(ASコードの単純なシンクブロックについて
synchronized
改変get
またはset
それがそう方法)消費状態遷移時間は、ユーザコードの実行時間よりも長くてもよいsynchronized
Java言語ヘビー操作です。
したがって、ロックは、軽量ロックそこ、最適化の多くを行っJDK1.6に表示されてロックする傾向があり、ロックのキャンセル、適応スピンロック、ロック粗大化(スピンロック1.4、デフォルトでは唯一の存在でありますJDK1.6)がデフォルトで有効になっている、閉じている、これらの操作は、競争上の問題を解決するために、スレッド間でデータを共有するために、より効率的です。
优质文章参考:[1] 从jvm源码看synchronized
[2] 深入理解Java并发之synchronized实现原理