网站项目书范文,竞价sem托管,网站背景,汕头市专注网站建设2.5 重量锁底层ObjectMonitor
需要去找到openjdk#xff0c;在百度中直接搜索openjdk#xff0c;第一个链接就是 找到ObjectMonitor的两个文件#xff0c;hpp#xff0c;cpp 先查看核心属性#xff1a;http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473…2.5 重量锁底层ObjectMonitor
需要去找到openjdk在百度中直接搜索openjdk第一个链接就是 找到ObjectMonitor的两个文件hppcpp 先查看核心属性http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/objectMonitor.hpp
ObjectMonitor() {
_header NULL; // header存储着MarkWord_count 0; // 竞争锁的线程个数
_waiters 0, // wait的线程个数
_recursions 0; // 标识当前synchronized锁重入的次数
_object NULL;
_owner NULL; // 持有锁的线程
_WaitSet NULL; // 保存wait的线程信息双向链表
_WaitSetLock 0 ;
_Responsible NULL ;
_succ NULL ;
_cxq NULL ; // 获取锁资源失败后线程要放到当前的单向链表中
FreeNext NULL ;
_EntryList NULL ; // _cxq以及被唤醒的WaitSet中的线程在一定机制下会放到EntryList中
_SpinFreq 0 ;
_SpinClock 0 ;
OwnerIsThread 0 ;
_previous_owner_tid 0;
}
适当的查看几个C中实现的加锁流程 http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/objectMonitor.cpp TryLock
int ObjectMonitor::TryLock (Thread * Self) {
for (;;) {
// 拿到持有锁的线程void * own _owner ;
// 如果有线程持有锁告辞
if (own ! NULL) return 0 ;
// 说明没有线程持有锁own是nullcmpxchg指令就是底层的CAS实现。
if (Atomic::cmpxchg_ptr (Self, _owner, NULL) NULL) {
// 成功获取锁资源
return 1 ;
}
// 这里其实重试操作没什么意义直接返回-1
if (true) return -1 ;
}
}
try_entry
bool ObjectMonitor::try_enter(Thread* THREAD) {
// 在判断_owner是不是当前线程
if (THREAD ! _owner) {
// 判断当前持有锁的线程是否是当前线程说明轻量级锁刚刚升级过来的情况
if (THREAD-is_lock_owned ((address)_owner)) {
_owner THREAD ;
_recursions 1 ;
OwnerIsThread 1 ;
return true;
}
// CAS操作尝试获取锁资源
if (Atomic::cmpxchg_ptr (THREAD, _owner, NULL) ! NULL) {// 没拿到锁资源告辞
return false;
}
// 拿到锁资源
return true;
} else {
// 将_recursions 1代表锁重入操作。
_recursions;
return true;
}
}
enter想方设法拿到锁资源如果没拿到挂起扔到_cxq单向链表中
void ATTR ObjectMonitor::enter(TRAPS) {
// 拿到当前线程
Thread * const Self THREAD ;
void * cur ;
// CAS走你
cur Atomic::cmpxchg_ptr (Self, _owner, NULL) ;
if (cur NULL) {
// 拿锁成功
return ;
}
// 锁重入操作
if (cur Self) {
// TODO-FIXME: check for integer overflow! BUGID 6557169._recursions ;
return ;
}
//轻量级锁过来的。
if (Self-is_lock_owned ((address)cur)) {
_recursions 1 ;
_owner Self ;
OwnerIsThread 1 ;
return ;
}
// 走到这了没拿到锁资源count
Atomic::inc_ptr(_count);
for (;;) {
jt-set_suspend_equivalent();
// 入队操作进到cxq中
EnterI (THREAD) ;
if (!ExitSuspendEquivalent(jt)) break ;
_recursions 0 ;
_succ NULL ;
exit (false, Self) ;
jt-java_suspend_self();
}
}// count--
Atomic::dec_ptr(_count);
}
EnterI
for (;;) {
// 入队
node._next nxt _cxq ;
// CAS的方式入队。
if (Atomic::cmpxchg_ptr (node, _cxq, nxt) nxt) break ;
// 重新尝试获取锁资源
if (TryLock (Self) 0) {
assert (_succ ! Self , invariant) ;
assert (_owner Self , invariant) ;
assert (_Responsible ! Self , invariant) ;
return ;
}
}
2.6 重量锁底层中的synchronized
synchronized的作用 就是在多线程中实现锁的作用保证synchronized锁住的代码只能有一个线程执行。synchronized用的锁是存在Java对象头里的。
对象头
在JVM中对象在内存中的布局分为3块对象头、实例数据和对齐填充。
实例数据 程序代码中定义的各种类型的字段内容。对齐填充 JVM要求对象的大小必须是8个字节的整数倍对象头已经是8的整数倍了如果实例数据没有8的整数倍就需要对齐填充来补全。
对象头 Mark Word 类型指针Klass pointer。
类型指针Klass pointer 用于标识JVM通过这个指针来确定这个对象是哪个类的实例。
Mark Word 用于储存对象自身的运行时数据例如对象的hashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程的ID、偏向时间戳等。在运行期间Mark Word里存储的数据会随着内部锁标志位的变化而变化。 无锁状态(01)
对象头开辟 25bit 的空间用来存储对象的 hashcode 4bit 用于存放分代年龄1bit 用来存放是否偏向锁的标识位2bit 用来存放锁标识位为01。
偏向锁状态(01)
还是开辟25bit 的空间其中23bit 用来存放线程ID2bit 用来存放 epoch4bit 存放分代年龄1bit 存放是否偏向锁标识 0表示无锁1表示偏向锁锁的标识位还是01。
轻量级锁状态(00)
开辟 30bit 的空间存放指向栈中锁记录的指针2bit 存放锁的标志位其标志位为00。
重量级锁状态(10)
30bit 的空间用来存放指向重量级锁的指针2bit 存放锁的标识位为10。
GC标记(11)
开辟30bit 的内存空间却没有占用2bit 空间存放锁标志位为11。
之所以这些标志位会变化是JVM对重量级锁的一种优化在jdk1.6 之前用synchronized 都是重量级锁 都需要底层操作系统的Mutex Lock互斥锁来实现这个是涉及到系统调用的会导致内核空间与用户空间的上下文切换很低效。切换原理
Synchronized是通过对象内部的一个叫做 监视器锁Monitor来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态这个成本非常高状态之间的转换需要相对比较长的时间这就是为什么Synchronized效率低的原因。因此这种依赖于操作系统Mutex Lock所实现的锁我们称之为 “重量级锁”。