Java的锁,绕不开synchronized或者Lock,在“表现层”是这两个关键字,可能对于使用者来说有这不同,或者那不同的问题,但是原理上都是字节码指令,所以如果要学习的话,不要太过分在意语法变更(并不是说不在意),而是应该去理解原理!
我们应该都知道了,java本身是调用Jvm的c++,然后c++去操作一个一个的字节码,而这些字节码的操作,叫做java字节码指令。在上层表示的synchronized跟lock,就是调用了对应的字节码指令,分别叫做_monitorenter、_monitorexit。从名称可以看出“监控进入同步区”、“监控退出同步区”。
先看一个对象,我们只需要记住这个对象的结构:
class BasicLock VALUE_OBJ_CLASS_SPEC {
friend class VMStructs;
private:
volatile markOop _displaced_header;
public:
markOop displaced_header() const { return _displaced_header; }
void set_displaced_header(markOop header) { _displaced_header = header; }
void print_on(outputStream* st) const;
// move a basic lock (used during deoptimization) :在优化过程中使用
void move_to(oop obj, BasicLock* dest);
static int displaced_header_offset_in_bytes() { return offset_of(BasicLock, _displaced_header); }
};
// A BasicObjectLock associates a specific Java object with a BasicLock.
It is currently embedded in an interpreter frame.
Because some machines have alignment restrictions on the control stack,
the actual space allocated by the interpreter may include padding words
after the end of the BasicObjectLock. Also, in order to guarantee
alignment of the embedded BasicLock objects on such machines, we
put the embedded BasicLock at the beginning of the struct.
翻译:
基本对象锁将特定的Java对象与基本锁关联起来。
它目前嵌入在解释器框架中。
因为某些机器对控制堆栈有对齐限制,
解释器分配的实际空间可以包括填充字。
基本对象锁定结束后。为了保证
在这样的机器上嵌入的基本锁对象的对齐
将嵌入的基本锁放在结构的开头。
*/
class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
friend class VMStructs;
private:
BasicLock _lock; // the lock, must be double word aligned
oop _obj; // object holds the lock;
public:
// Manipulation 操纵
oop obj() const { return _obj; }
void set_obj(oop obj) { _obj = obj; }
BasicLock* lock() { return &_lock; }
// Note: Use frame::interpreter_frame_monitor_size() for the size of BasicObjectLocks
// in interpreter activation frames since it includes machine-specific padding.
在解释器激活框架中,因为它包括特定于机器的填充。
static int size() { return sizeof(BasicObjectLock)/wordSize; }
// GC support
void oops_do(OopClosure* f) { f->do_oop(&_obj); }
static int obj_offset_in_bytes() { return offset_of(BasicObjectLock, _obj); }
static int lock_offset_in_bytes() { return offset_of(BasicObjectLock, _lock); }
};
然后直接贴锁指令源码:
/* monitorenter and monitorexit for locking/unlocking an object */
//这两个指令就是对一个对象进行上锁/解锁的操作
CASE(_monitorenter): {
oop lockee = STACK_OBJECT(-1);
// derefing's lockee ought to provoke implicit null check
// derefing的锁定,应引起隐式零校验
CHECK_NULL(lockee);
// find a free monitor or one already allocated for this object
// 找一个空闲监控或者一个已经分配了的对象
// if we find a matching object then we need a new monitor
// 如果我们找到匹配对象,那么我们需要一个新的监视器
// since this is recursive enter
// 因为这是递归输入
/*monitor_base,这个直接返回了_monitor_base,注释的解释是base of monitors on the native stack,即:本地堆栈的基础监视器*/
BasicObjectLock* limit = istate->monitor_base();
/*
stack_base 直接返回了_stack_base,注释解释为:base of expression stack
即:基础栈的表达式
*/
BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
/*
此处的操作应该是从基础栈取对象信息,直到该锁锁定的边界位置
如果从基础栈中获取的最近栈的对象为空,则将最近的指针指向entry
如果从基础栈中获取的最近栈的对象为为lock的对象(?),则跳出循环
*/
BasicObjectLock* entry = NULL;
while (most_recent != limit ) {
if (most_recent->obj() == NULL)
entry = most_recent;
else if (most_recent->obj() == lockee)
break;
most_recent++;
}
/*
如果入口指针不为null,需要将指针指向的对象,填充被锁对象的值
获取入口指针的未锁定对象头;
将对象头设置到被锁对象的指针上
从而完成被锁内容的赋值操作
保证原子性的cas操作,比较entry跟lockee的对象头地址是否相同,如果相同把entry 地址赋值给displaced(对象头地址);
*/
if (entry != NULL) {
entry->set_obj(lockee);
markOop displaced = lockee->mark()->set_unlocked();
entry->lock()->set_displaced_header(displaced);
if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
// Is it simple recursive case?
//THREAD是一个指向一个线程的定义,如果是一个偏向锁,将所的对象头置空
if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
entry->lock()->set_displaced_header(NULL);
} else {
//这里大概是轻量锁/重量锁?具体看下边这个方法的源码
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
}
//更新pc寄存器,同时继续下一条指令?
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
} else {
//entry为空则设置状态为其他监视器,并更新pc寄存器同时返回0
istate->set_msg(more_monitors);
UPDATE_PC_AND_RETURN(0); // Re-execute
}
}
InterpreterRuntime::monitorenter:
//%note monitor_1
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
if (PrintBiasedLockingStatistics) {
Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
}
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);
}
assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
"must be NULL or an object");
#ifdef ASSERT
thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
//如果使用偏向锁
if (UseBiasedLocking) {
//同步块处于安全点上:表明jvm在此处是安全的,可以暂停
if (!SafepointSynchronize::is_at_safepoint()) {
//这的意思应该是对偏向锁进行cas交换
BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
return;
}
} else {
assert(!attempt_rebias, "can not rebias toward VM thread");
BiasedLocking::revoke_at_safepoint(obj);//废弃安全点
}
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
}
slow_enter (obj, lock, THREAD) ;
}
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");
/* We can revoke the biases of anonymously-biased objects:
efficiently enough that we should not cause these revocations to
update the heuristics because doing so may cause unwanted bulk
revocations (which are expensive) to occur.
翻译:
我们可以撤销匿名偏见对象的偏见:
足够有效,我们不应该造成这些撤销
更新试探法,因为这样做可能会导致不必要的体积。
撤销(很昂贵)发生。
*/
markOop mark = obj->mark();
if (mark->is_biased_anonymously() && !attempt_rebias) {//如果是隐士的偏向锁,尝试重置
/*
We are probably trying to revoke the bias of this object due to
an identity hash code computation. Try to revoke the bias
without a safepoint. This is possible if we can successfully
compare-and-exchange an unbiased header into the mark word of
the object, meaning that no other thread has raced to acquire
the bias of the object.
翻译:我们可能试图撤销这个对象的偏向,由于
一个身份哈希代码计算。试图没有安全点情况下消除偏向
。这是可能的,我们能成功比较和交换一个无偏标头到标记字
该对象,即没有其他线程竞争已经获取当前对象的偏向。
PS:实际上可以看出来,此处就是偏向锁的cas过程,找到对象头中关于线程id地址的交换
*/
markOop biased_value = mark;//获取对象头
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());//设置年龄代
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);//对非偏向元素进行地址cas操作
if (res_mark == biased_value) {
return BIAS_REVOKED;
}
} else if (mark->has_bias_pattern()) {
Klass* k = Klass::cast(obj->klass());
markOop prototype_header = k->prototype_header();//获取对象头元素信息
if (!prototype_header->has_bias_pattern()) {
/*
This object has a stale bias from before the bulk revocation
for this data type occurred. It's pointless to update the
heuristics at this point so simply update the header with a
CAS. If we fail this race, the object's bias has been revoked
by another thread so we simply return and let the caller deal
with it.
翻译:
此对象在批量撤销之前具有过时的偏向。
这一点上简单地用cas交换,为此发生了数据类型的更新是没有意义的。
如果我们失败了,这个对象的偏见已经被撤销了。
有了它,通过另一个线程,我们简单地返回并让调用方处理。
*/
markOop biased_value = mark;///获取偏向值
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);///执行cas操作
assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
return BIAS_REVOKED;
} else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
if (attempt_rebias) {
assert(THREAD->is_Java_thread(), "");
markOop biased_value = mark;
markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED_AND_REBIASED;
}
} else {
markOop biased_value = mark;
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED;
}
}
}
}
/** Interpreter/Compiler Slow Case
This routine is used to handle interpreter/compiler slow case
We don't need to use fast path here, because it must have been
failed in the interpreter/compiler code.
编译器/解释器慢案例
本例程是用来解释/编译慢实例句柄
我们不需要使用路径,因为它几乎是在这里,必须有
在解释器的代码/编译失败。
*/
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()) {
// Anticipate successful CAS -- the ST of the displaced mark must
// be visible <= the ST performed by the CAS.
lock->set_displaced_header(mark);
if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
TEVENT (slow_enter: release stacklock) ;
return ;
}
// Fall through to inflate() ...
} 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");
lock->set_displaced_header(NULL);
return;
}
#if 0
// The following optimization isn't particularly useful.
if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
lock->set_displaced_header (NULL) ;
return ;
}
#endif
/** The object header will never be displaced to this lock,
so it does not matter what the value is, except that it
must be non-zero to avoid looking like a re-entrant lock,
and must not look locked either.
对象头将永远不会移位到这个锁,
所以不管价值是什么,除了它
必须是非零,以避免看起来像一个可重入的锁,
也不能锁上。
*/
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();
while (most_recent != limit ) {
if ((most_recent)->obj() == lockee) {
BasicLock* lock = most_recent->lock();
markOop header = lock->displaced_header();
most_recent->set_obj(NULL);
// If it isn't recursive we either must swap old header or call the runtime
if (header != NULL) {
if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) {
// restore object for the slow case
most_recent->set_obj(lockee);
CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
}
}
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
}
most_recent++;
}
// Need to throw illegal monitor state exception
CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
ShouldNotReachHere();
}
PS:源码只是解释,接下来,将形成规范化的步骤与流程,解释锁