本文共 5707 字,大约阅读时间需要 19 分钟。
synchronized是JVM 内部的Instrinsic Lock。所以偏斜锁、轻量级锁、重量级锁的代码实现不在核心类库部分,而是JVM 代码中。
java代码运行分两种模式,解释型: javac经编译成字节码后,运行时字节码被jvm加载、解释为机器码进行解释执行。编译型:javac编译成字节码后,jvm的JIT(java 即时编译器)会根据jvm代码情况,执行效率使用内联、虚化等规则,在运行时会将热点代码编译成机器码进行优化,提高执行效率
1.[解释器版本-最新代码](http://hg.openjdk.java.net/jdk/jdk/file/6659a8f57d78/src/hotspot/share/interpreter/interpreterRuntime.cpp)2.为便于理解,这里专注于[通用基类-最新代码](http://hg.openjdk.java.net/jdk/jdk/file/6659a8f57d78/src/hotspot/share/runtime/)实现
a.解释器和编译器运行时的基类:sharedRuntime.cpp/hpp,UseBiasedLocking是一个检查,因为,在JVM 启动时,我们可以指定是否开启偏斜锁。偏斜锁不适合所有场景,撤销(revoke)是比较重的行为,只有当存在较多真正竞争的synchronized块时,才能体现出明显改善。 Handle h_obj(THREAD, obj); if (UseBiasedLocking) { // Retry fast entry if bias is revoked to avoid unnecessary inflation ObjectSynchronizer::fast_enter(h_obj, lock, true, CHECK); } else { ObjectSynchronizer::slow_enter(h_obj, lock, CHECK); }b.另一方面,偏斜锁会延缓JIT 预热进程,因此大多数性能测试会显式地关闭偏斜锁 命令:开启 -XX:+UseBiasedLocking 关闭:-XX:+UseBiasedLocking
(1)类似faster_enter实现,解释器或动态编译器,都是拷贝这的基础逻辑,所以如果我们修改这个部分逻辑,要保证一致性。修改要谨慎,微小问题都可能造成死锁或正确性问题。(2)逻辑如下:bisedLocking定义了偏斜锁的相关操作,revoke_and_rebias是获取偏斜锁的入口方法,revoke_at_safepoint则定义了当检测到安全点是的处理逻辑;如果获取偏斜锁失败,则进入slow_enter;该方法同样检查是否开启偏斜锁,但从代码看,如果关闭偏斜锁,不会进入该方法,所以其是个额外保障性检查(3)核心代码如下: void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) { if (UseBiasedLocking) { if (!SafepointSynchronize::is_at_safepoint()) { 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); }
仔细查看 synchronized.cpp里,会发现不仅仅是synchronized的逻辑,包括从本地代码(JNI),出发Monitor 动作,都可以看到
流程:设置 Displaced Header,然后利用cas_set_mark设置对象Mark Word,如果成功就获取轻量级锁;否则Displaced Header,然后进入锁膨胀阶段,具体实现在inflated方法中, void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) { markOop mark = obj->mark(); if (mark->is_neutral()) { // 将目前的Mark Word复制到Displaced Header上 lock->set_displaced_header(mark); // 利用CAS设置对象的Mark Word if (mark == obj()->cas_set_mark((markOop) lock, mark)) { TEVENT(slow_enter: release stacklock); return; } // 检查存在竞争 } else if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { // 清除 lock->set_displaced_header(NULL); return; } // 重置Displaced Header lock->set_displaced_header(markOopDesc::unused_mark()); ObjectSynchronizer::inflate(THREAD, obj(), inflate_cause_monitor_enter)->enter(THREAD); }
- 6。deflate_idle_monitors(参考源码是分析锁降级逻辑的入口,这部分行为会进行持续改进,因为其逻辑实在安全点内进行,处理不当会托长JVM停顿时间(STW,stop-the-world)的时间- 7. fast_exit或slow_exit是对应的锁释放逻辑(参考源码)
类图
上述显示,这些锁都未实现Lock接口。ReadWriteLock是一个单独接口,通常代表一对锁(共享读锁,互斥写锁)。标准库提供了再入版本的读写锁(ReentrantReadWriteLock),对应的语义同ReentrantLock相似。
StampedLock也是单独类型,其不支持再入性予以,即不是以持有锁的线程为单位。其在提供类似读写锁的同时,还支持基于假设的优化读模式。逻辑是先试着读,然后通过validate方法确认是否进入写模式,如果未进入,则成功避免开销;如果进入,则尝试获取读锁。例子如下
public class StampedSample { private final StampedLock sl = new StampedLock(); void mutate() { long stamp = sl.writeLock(); try { write(); } finally { sl.unlockWrite(stamp); } } Data access() { long stamp = sl.tryOptimisticRead(); Data data = read(); if (!sl.validate(stamp)) { stamp = sl.readLock(); try { data = read(); } finally { sl.unlockRead(stamp); } } return data; } // …}
读写锁的需求场景
1.ReentrantLock和synchronized简单实用,但行为有一定局限性,通俗说就是”霸道”,要么不占用,要么独占(写时独占保证数据一致性,读时独占则严重影响线程并发).实际场景,不需要大量竞争的写操作,而是以并发读取为主。因此出现ReentrantReadWriteLock等2.java并发包提供的读写锁扩展了锁的能力,其原理是多个读操作不需要互斥,读操作不会更改数据,不存在相互干扰,写操作则会导致并发一致性问题。因此,写线程之间、读写线程之间,需要精心设计互斥逻辑3.以下是使用ReentrantReadWriteLock,解决数据量打,并发读多,并发写少,能够比纯同步版本凸显优势。运行过程中,读锁试图锁定时,写锁是被某个线程持有,读锁则无法获取,只能等待对方操作结束,这样保证不会读取到有争议的数据。读锁可以并发访问。4.这里writeLock和unLockWrite一定保证成对调用5.例子如下:public class RWSample { private final Mapm = new TreeMap<>(); private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); public String get(String key) { r.lock(); System.out.println("读锁锁定!"); try { return m.get(key); } finally { r.unlock(); } } public String put(String key, String entry) { w.lock(); System.out.println("写锁锁定!"); try { return m.put(key, entry); } finally { w.unlock(); } } // … }
转载地址:http://uhjzb.baihongyu.com/