Synchronized锁了个什么玩意
共 11852字,需浏览 24分钟
·
2023-06-30 11:25
微信公众号:saikou4java
问题或建议,请公众号留言;
Synchronized锁了个什么玩意
先大胆假设,如果让我们自己实现一个锁一样的功能,应该怎么实现呢。某个资源,我在使用,别人就不能用,必须等我使用完毕才能用,假设使用全局变量来控制。
使用伪代码完成上面的逻辑
// 定义一个静态变量
static boolean isLock = false;
// 加锁的方法
public static void lock() {
while (true) {
// 如果锁是打开的,获取并关闭锁,否则循环等待
if (!isLock) {
isLock = true;
break;
}
}
}
// 解锁的方法
private static void release() {
isLock = false;
}
伪代码是不可用的,可以猜想一下synchronized是不是也是通过某个标志来控制的呢,我们写一个方法,通过生成的.class文件来查看一下
public static void main(String[] args) {
int a = 0;
synchronized (App3.class){
a++;
}
}
对应的.class文件反编译为
public static void main(String[] args) {
int a = 0;
Class var2 = App3.class;
synchronized(App3.class) {
int var5 = a + 1;
}
}
对应的jvm指令为
public static void main(java.lang.String[]);
Code:
0: iconst_0 // a值入栈
1: istore_1 // a值出栈,保存到局部变量中
2: ldc // 常量池中的引用var2入栈
4: dup // 复制栈顶的var2,再次入栈
5: astore_2 // var2出栈,保存到局部变量中
6: monitorenter // var2出栈,获取对象监视器
7: iinc 1, 1 // 局部变量a加1
10: aload_2 // 从局部变量中获取var2,再次入栈
11: monitorexit // var2出栈,释放并退出对象监视器
12: goto 20 // 无条件跳转到指定位置
... // 异常处理
20: return // void函数返回
从上面的指令可以看出,synchronized关键字转化为了指令monitorenter和monitorexit,那么我们只需要知道这两条指令是什么意思就行了。
monitorenter和monitorexit是什么
java虚拟机规范中对monitorenter描述为:Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref.
谷歌翻译一下就是:每个object都与一个monitor关联。monitor只有在拥有所有者的情况下才会被锁定。执行monitorenter的线程会尝试获得与objectref关联的监视器的所有权。
那么我们有两个问题
monitors是个什么东西
monitor和object是怎么关联的
首先看monitors是什么东西,在软件的世界里,只有代码,既然jvm也是一个软件,那我们找到monitors的代码就知道了。这个代码在openjdk11\src\hotspot\share\runtime包下,有个类叫objectMonitor.cpp。虽然是个2446行的c++代码,完全看不懂,但是只要找到关键方法就可以,此处可以求助陈师兄--!。
void ObjectMonitor::enter(TRAPS) {
// 看到Thread倍感情切,也就能看懂这个单词了。
// 定义当前线程self
Thread * const Self = THREAD;
// 和java中的cas类似,将对象的owner设置为当前线程self
// 这一步就是用来获取锁的
void * cur = Atomic::cmpxchg(Self, &_owner, (void*)NULL);
// 获取锁失败
if (cur == NULL) {
assert(_recursions == 0, "invariant");
assert(_owner == Self, "invariant");
return;
}
// 同一个线程可以重复获取锁
// _recursions记录获取锁的次数
if (cur == Self) {
_recursions++;
return;
}
// 第一次获取锁成功进行初始化,设置了_recursions
// 断言只有在_recursions==0的时候才可以获取锁
if (Self->is_lock_owned ((address)cur)) {
assert(_recursions == 0, "internal state error");
_recursions = 1;
_owner = Self;
return;
}
assert(Self->_Stalled == 0, "invariant");
Self->_Stalled = intptr_t(this);
if (Knob_SpinEarly && TrySpin (Self) > 0) {
assert(_owner == Self, "invariant");
assert(_recursions == 0, "invariant");
assert(((oop)(object()))->mark() == markOopDesc::encode(this), "invariant");
Self->_Stalled = 0;
return;
}
assert(_owner != Self, "invariant");
assert(_succ != Self, "invariant");
assert(Self->is_Java_thread(), "invariant");
JavaThread * jt = (JavaThread *) Self;
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
assert(jt->thread_state() != _thread_blocked, "invariant");
assert(this->object() != NULL, "invariant");
assert(_count >= 0, "invariant");
Atomic::inc(&_count);
JFR_ONLY(JfrConditionalFlushWithStacktrace<EventJavaMonitorEnter> flush(jt);)
EventJavaMonitorEnter event;
if (event.should_commit()) {
event.set_monitorClass(((oop)this->object())->klass());
event.set_address((uintptr_t)(this->object_addr()));
}
{
JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
Self->set_current_pending_monitor(this);
DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_enter()) {
JvmtiExport::post_monitor_contended_enter(jt, this);
}
OSThreadContendState osts(Self->osthread());
ThreadBlockInVM tbivm(jt);
for (;;) {
jt->set_suspend_equivalent();
EnterI(THREAD);
if (!ExitSuspendEquivalent(jt)) break;
_recursions = 0;
_succ = NULL;
exit(false, Self);
jt->java_suspend_self();
}
Self->set_current_pending_monitor(NULL);
}
Atomic::dec(&_count);
assert(_count >= 0, "invariant");
Self->_Stalled = 0;
assert(_recursions == 0, "invariant");
assert(_owner == Self, "invariant");
assert(_succ != Self, "invariant");
assert(((oop)(object()))->mark() == markOopDesc::encode(this), "invariant");
DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt);
if (JvmtiExport::should_post_monitor_contended_entered()) {
JvmtiExport::post_monitor_contended_entered(jt, this);
}
if (event.should_commit()) {
event.set_previousOwner((uintptr_t)_previous_owner_tid);
event.commit();
}
OM_PERFDATA_OP(ContendedLockAttempts, inc());
}
虽然上面的方法完全看不懂,但是我们已经好像得到了想要的东西,monitor通过cas的方式把owner设置为当前线程,如果owner已经被其他线程设置过了,直接设置失败,即获取锁失败,好像跟我们伪代码里写的那个isLock作用一样,原来jvm你也搞了个变量来控制。在第一次获取锁成功的时候,设置了recursions = 1,并且断言了只有recursions = 0的时候才可以获取成功,为了保证锁可以被一个线程获取多次,使用了recursions ++,可以猜测,释放锁肯定使用了recursions --,直到减为0,也就是说一个线程获取的锁必须全部释放了,其他线程才有可能调用添加锁的方法。
再来看看释放锁的方法
void ObjectMonitor::exit(bool not_suspended, TRAPS) {
Thread * const Self = THREAD;
// 先判断当前线程是否等于owner
// 有两种情况
if (THREAD != _owner) {
// 轻量级锁情况的释放
if (THREAD->is_lock_owned((address) _owner)) {
assert(_recursions == 0, "invariant");
_owner = THREAD;
_recursions = 0;
} else {
// 真的不是一个线程,直接退出
// 会抛出IllegalMonitorStateException异常
// 某个线程的锁不能由其他线程释放
TEVENT(Exit - Throw IMSX);
assert(false, "Non-balanced monitor enter/exit! Likely JNI locking");
return;
}
}
if (_recursions != 0) {
// 加多少次锁就释放多少次
_recursions--;
TEVENT(Inflated exit - recursive);
return;
}
if ((SyncFlags & 4) == 0) {
_Responsible = NULL;
}
#if INCLUDE_JFR
if (not_suspended && EventJavaMonitorEnter::is_enabled()) {
_previous_owner_tid = JFR_THREAD_ID(Self);
}
#endif
for (;;) {
assert(THREAD == _owner, "invariant");
if (Knob_ExitPolicy == 0) {
// 这一步就是用来释放锁的,看方法名字
OrderAccess::release_store(&_owner, (void*)NULL);
OrderAccess::storeload();
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
TEVENT(Inflated exit - simple egress);
return;
}
TEVENT(Inflated exit - complex egress);
if (!Atomic::replace_if_null(THREAD, &_owner)) {
return;
}
TEVENT(Exit - Reacquired);
} else {
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
OrderAccess::release_store(&_owner, (void*)NULL);
OrderAccess::storeload();
if (_cxq == NULL || _succ != NULL) {
TEVENT(Inflated exit - simple egress);
return;
}
if (!Atomic::replace_if_null(THREAD, &_owner)) {
TEVENT(Inflated exit - reacquired succeeded);
return;
}
TEVENT(Inflated exit - reacquired failed);
} else {
TEVENT(Inflated exit - complex egress);
}
}
guarantee(_owner == THREAD, "invariant");
ObjectWaiter * w = NULL;
int QMode = Knob_QMode;
if (QMode == 2 && _cxq != NULL) {
w = _cxq;
assert(w != NULL, "invariant");
assert(w->TState == ObjectWaiter::TS_CXQ, "Invariant");
ExitEpilog(Self, w);
return;
}
if (QMode == 3 && _cxq != NULL) {
w = _cxq;
for (;;) {
assert(w != NULL, "Invariant");
ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w);
if (u == w) break;
w = u;
}
assert(w != NULL, "invariant");
ObjectWaiter * q = NULL;
ObjectWaiter * p;
for (p = w; p != NULL; p = p->_next) {
guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
p->TState = ObjectWaiter::TS_ENTER;
p->_prev = q;
q = p;
}
ObjectWaiter * Tail;
for (Tail = _EntryList; Tail != NULL && Tail->_next != NULL;
Tail = Tail->_next)
if (Tail == NULL) {
_EntryList = w;
} else {
Tail->_next = w;
w->_prev = Tail;
}
}
if (QMode == 4 && _cxq != NULL) {
w = _cxq;
for (;;) {
assert(w != NULL, "Invariant");
ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w);
if (u == w) break;
w = u;
}
assert(w != NULL, "invariant");
ObjectWaiter * q = NULL;
ObjectWaiter * p;
for (p = w; p != NULL; p = p->_next) {
guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
p->TState = ObjectWaiter::TS_ENTER;
p->_prev = q;
q = p;
}
if (_EntryList != NULL) {
q->_next = _EntryList;
_EntryList->_prev = q;
}
_EntryList = w;
}
w = _EntryList;
if (w != NULL) {
assert(w->TState == ObjectWaiter::TS_ENTER, "invariant");
ExitEpilog(Self, w);
return;
}
w = _cxq;
if (w == NULL) continue;
for (;;) {
assert(w != NULL, "Invariant");
ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w);
if (u == w) break;
w = u;
}
TEVENT(Inflated exit - drain cxq into EntryList);
assert(w != NULL, "invariant");
assert(_EntryList == NULL, "invariant");
if (QMode == 1) {
ObjectWaiter * s = NULL;
ObjectWaiter * t = w;
ObjectWaiter * u = NULL;
while (t != NULL) {
guarantee(t->TState == ObjectWaiter::TS_CXQ, "invariant");
t->TState = ObjectWaiter::TS_ENTER;
u = t->_next;
t->_prev = u;
t->_next = s;
s = t;
t = u;
}
_EntryList = s;
assert(s != NULL, "invariant");
} else {
_EntryList = w;
ObjectWaiter * q = NULL;
ObjectWaiter * p;
for (p = w; p != NULL; p = p->_next) {
guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
p->TState = ObjectWaiter::TS_ENTER;
p->_prev = q;
q = p;
}
}
if (_succ != NULL) continue;
w = _EntryList;
if (w != NULL) {
guarantee(w->TState == ObjectWaiter::TS_ENTER, "invariant");
ExitEpilog(Self, w);
return;
}
}
}
基本就认识几个单词,再次@陈师兄--!,但是可以看到跟我们猜想的过程一样,释放锁的时候先recursions--,再把owner重置。所以synchronized关键字锁的是个什么东西呢,锁的是object,锁的是object关联的monitor,锁的是object关联的monitor里面的owner,都说得过去。
那monitor和object是怎么关联的
java对象之间的关系,有组合有引用有继承有实现,但是我们从来没有见过一个monitor这样的类,猜测一下,如果我们想让一个对象关联另一个对象,传入另一个对象的引用就行了,引用就是这个对象在内存中的地址值,所以我们的对象可能也在某个地方保存了monitor的地址值,要搞清楚这个问题,必须知道对象里的结构是什么。
根据《深入理解java虚拟机》,在HotSpot虚拟机中,对象在内存中存储的布局可以分为3个区域,对象头(Header),实例数据(Instance Data),对齐填充(Padding)
Header又分为两部分,第一部分用于存储对象自身的运行时数据,如哈希码,GC分带年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等,这部分官方称为Mark Word。第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
关于对象头中的信息可以查看 openJdk中 markOop.hpp的注释。
我们就把对象头打印出来看看,根据不同的锁状态,对象头中的数据代表的含义会发生变化,64位intel cpu使用小端存储,最高2bit10就是上面注释中的lock,代表对象持有重量级锁,其余62位代表monitor的指针。
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) da f5 28 1d (11011010 11110101 00101000 00011101) (489223642)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
后记
以上内存不保证任何正确性,纯属猜测,没有经过证明。
下面的是我的公众号二维码图片,欢迎关注。