Эта статья реализует третью статью для базового синхронизированного лечения, содержание является легкой реализацией блокировки.
Легкие замки не сложные, многие из которых вБлокировка смещенияупоминается в статье,Будет некоторое совпадение с содержанием этой статьи.
Кроме того, фон и базовый процесс легкого блокировки находятся вВведениеобъяснил в.Настоятельно рекомендуется прочитать эту статью на основе прочтения двух статей.
В этой серии статей мы рассмотрим HotSpotsynchronized
Всесторонний анализ реализации блокировки, включая анализ принципа и исходного кода предвзятых блокировок, облегченных блокировок и тяжелых блокировок, блокировок, разблокировок и процессов обновления блокировок.synchronized
Некоторая помощь от однокурсников на дороге. В основном включают следующие статьи:
Реализация Deadly Synchronized Bottom — Введение
Реализация нижнего уровня Deadly Synchronized — предвзятая блокировка
Базовая реализация Deadly Synchronized — облегченная блокировка
Реализация нижнего уровня Deadly Synchronized — усиленная блокировка
Больше статей смотрите в личном блоге:GitHub.com/farmer Джон Брат…
Эта статья разделена на две части:
1. Облегченный процесс получения блокировки
2. Легкий процесс открытия замка
Версия JVM, которую я вижу, это jdk8u, конкретный номер версии и код можно найти вздесьВидеть.
Облегченный процесс получения блокировки
Приступим к анализу процесса получения легковесного замка, код находится вbytecodeInterpreter.cpp#1816.
CASE(_monitorenter): {
oop lockee = STACK_OBJECT(-1);
...
if (entry != NULL) {
...
// 上面省略的代码中如果CAS操作失败也会调用到InterpreterRuntime::monitorenter
// traditional lightweight locking
if (!success) {
// 构建一个无锁状态的Displaced Mark Word
markOop displaced = lockee->mark()->set_unlocked();
// 设置到Lock Record中去
entry->lock()->set_displaced_header(displaced);
bool call_vm = UseHeavyMonitors;
if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
// 如果CAS替换不成功,代表锁对象不是无锁状态,这时候判断下是不是锁重入
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
entry->lock()->set_displaced_header(NULL);
} else {
// CAS操作失败则调用monitorenter
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
}
}
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
} else {
istate->set_msg(more_monitors);
UPDATE_PC_AND_RETURN(0); // Re-execute
}
}
Если объект блокировки не привязан к режиму или был смещен к другим потокам, тогдаsuccess
заfalse
. В это время будет построено свободное от блокировки состояние.mark word
установлен вLock Record
Иди, мы называемLock Record
Объекты хранятся вmark word
Поле называетсяDisplaced Mark Word
.
CAS дает сбой, если текущее состояние блокировки не является свободным от блокировки. Если это повторный вход в блокировку, тоLock Record
изDisplaced Mark Word
Установить какnull
.
Мы видим демонстрацию, повторенную трижды в демонстрации при получении блокировки,
synchronized(obj){
synchronized(obj){
synchronized(obj){
}
}
}
Предполагая, что состояние замка является упрощенным, следующий рисунок отражаетmark word
и стек потоковLock Record
состояние, вы можете видеть, что стек потока справа содержит 3 указателя на текущий объект блокировкиLock Record
. Высший порядок в стекеLock Record
Выделяется при первом получении блокировки. ТотDisplaced Mark word
Значением блокировки является объект блокировки.mark word
, последующий повторный вход в блокировку выделит блокировку в стеке потокаDisplaced Mark word
заnull
изLock Record
.
Почему JVM решила добавить в стек потоковDisplaced Mark word
нулевойLock Record
для представления количества повторных входов? В первую очередь необходимо фиксировать количество повторных входов в замок, потому что каждой разблокировке нужно соответствовать замку, и когда количество разблокировок равно количеству замков, блокировка действительно снимается, то есть блокировка необходимо использовать при разблокировке количество записей. Простое решение — записывать количество повторных входов в блокировку в заголовке объекта.mark word
в, ноmark word
ограничен по размеру и больше не может хранить информацию. Другой вариант — просто создатьLock Record
И записывать в него количество повторных входов.Думаю, причина, по которой Hotspot этого не сделал, заключается в учете эффекта эффективности: каждый раз, когда повторный вход получает блокировку, необходимо пройтись по стеку потока, чтобы найти соответствующую блокировку.Lock Record
, а затем измените его значение.
Так что, в конце концов, Hotspot добавляет блокировку каждый раз, когда получает блокировку.Lock Record
Представлен переинтересованный замок.
см. далееInterpreterRuntime::monitorenter
метод
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
...
Handle h_obj(thread, elem->obj());
assert(Universe::heap()->is_in_reserved_or_null(h_obj()),
"must be NULL or an object");
if (UseBiasedLocking) {
// Retry fast entry if bias is revoked to avoid unnecessary inflation
ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}
...
IRT_END
fast_enter
В статье был проанализирован процесс смещения блокировки, если в настоящее время он находится в режиме смещения и смещенный поток все еще использует блокировку, он заблокирует блокировку.mark word
Перейдите в состояние облегченной блокировки и в то же время изменит стек смещенного потока.Lock Record
Доработан до формы, соответствующей облегченному замку. расположение кодаbiasedLocking.cpp#212.
// 线程还存活则遍历线程栈中所有的Lock Record
GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(biased_thread);
BasicLock* highest_lock = NULL;
for (int i = 0; i < cached_monitor_info->length(); i++) {
MonitorInfo* mon_info = cached_monitor_info->at(i);
// 如果能找到对应的Lock Record说明偏向的线程还在执行同步代码块中的代码
if (mon_info->owner() == obj) {
...
// 需要升级为轻量级锁,直接修改偏向线程栈中的Lock Record。为了处理锁重入的case,在这里将Lock Record的Displaced Mark Word设置为null,第一个Lock Record会在下面的代码中再处理
markOop mark = markOopDesc::encode((BasicLock*) NULL);
highest_lock = mon_info->lock();
highest_lock->set_displaced_header(mark);
} else {
...
}
}
if (highest_lock != NULL) {
// 修改第一个Lock Record为无锁状态,然后将obj的mark word设置为执行该Lock Record的指针
highest_lock->set_displaced_header(unbiased_prototype);
obj->release_set_mark(markOopDesc::encode(highest_lock));
...
} else {
...
}
мы видимslow_enter
Процесс.
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
markOop mark = obj->mark();
assert(!mark->has_bias_pattern(), "should not see bias pattern here");
// 如果是无锁状态
if (mark->is_neutral()) {
//设置Displaced Mark Word并替换对象头的mark word
lock->set_displaced_header(mark);
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
TEVENT (slow_enter: release stacklock) ;
return ;
}
} else
if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
assert(lock != mark->locker(), "must not re-lock the same lock");
assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
// 如果是重入,则设置Displaced Mark Word为null
lock->set_displaced_header(NULL);
return;
}
...
// 走到这一步说明已经是存在多个线程竞争锁了 需要膨胀为重量级锁
lock->set_displaced_header(markOopDesc::unused_mark());
ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}
Облегченный процесс открытия замка
CASE(_monitorexit): {
oop lockee = STACK_OBJECT(-1);
CHECK_NULL(lockee);
// derefing's lockee ought to provoke implicit null check
// find our monitor slot
BasicObjectLock* limit = istate->monitor_base();
BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
// 从低往高遍历栈的Lock Record
while (most_recent != limit ) {
// 如果Lock Record关联的是该锁对象
if ((most_recent)->obj() == lockee) {
BasicLock* lock = most_recent->lock();
markOop header = lock->displaced_header();
// 释放Lock Record
most_recent->set_obj(NULL);
// 如果是偏向模式,仅仅释放Lock Record就好了。否则要走轻量级锁or重量级锁的释放流程
if (!lockee->mark()->has_bias_pattern()) {
bool call_vm = UseHeavyMonitors;
// header!=NULL说明不是重入,则需要将Displaced Mark Word CAS到对象头的Mark Word
if (header != NULL || call_vm) {
if (call_vm || Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) {
// CAS失败或者是重量级锁则会走到这里,先将obj还原,然后调用monitorexit方法
most_recent->set_obj(lockee);
CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
}
}
}
//执行下一条命令
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
}
//处理下一条Lock Record
most_recent++;
}
// Need to throw illegal monitor state exception
CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
ShouldNotReachHere();
}
Когда облегченный замок отпущен, его необходимоDisplaced Mark Word
заменен заголовком объектаmark word
середина. Если CAS не работает или является тяжеловесной блокировкой, введитеInterpreterRuntime::monitorexit
метод.
//%note monitor_1
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem))
Handle h_obj(thread, elem->obj());
...
ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread);
// Free entry. This must be done here, since a pending exception might be installed on
//释放Lock Record
elem->set_obj(NULL);
...
IRT_END
monitorexit
вызов завершенslow_exit
метод, выпускLock Record
.
void ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {
fast_exit (object, lock, THREAD) ;
}
void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
...
markOop dhw = lock->displaced_header();
markOop mark ;
if (dhw == NULL) {
// 重入锁,什么也不做
...
return ;
}
mark = object->mark() ;
// 如果是mark word==Displaced Mark Word即轻量级锁,CAS替换对象头的mark word
if (mark == (markOop) lock) {
assert (dhw->is_neutral(), "invariant") ;
if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) {
TEVENT (fast_exit: release stacklock) ;
return;
}
}
//走到这里说明是重量级锁或者解锁时发生了竞争,膨胀后调用重量级锁的exit方法。
ObjectSynchronizer::inflate(THREAD, object)->exit (true, THREAD) ;
}
В этом методе сначала определите, является ли это облегченным замком, если это облегченный замок, он будет замененmark word
, иначе раздуть в тяжеловесный замок и вызватьexit
метод, и связанная с ним логика будет объяснена в статье о тяжелых замках.