Open yisaca opened 4 years ago
#### 偏向锁的获取 # 1 从线程栈的栈底遍历尝试寻找一个【objectRef为NULL的RecordLock】或者【当前线程已经用在当前锁对象上的LockRecord】 1.1 如果碰到【当前线程已经用在当前锁对象上的LockRecord】,直接退出遍历(定位到之前的LockRecord) 1.2 如果【objectRef为NULL的RecordLock】,继续向上遍历,直至找到离栈顶近的【objectRef为NULL的RecordLock】 //这个遍历策略保证最先获取锁的一次调用的LockRecord更加靠近栈顶 # 2 如果没有找到到空闲的LockRecord,则说明栈上LockRecord不够用,需要重新执行 # 3 找到objectRef为NULl的LockRecord,并记为entry 3.1 更新LockRecord的objectRef指向锁对象 3.2 初始化标记字段success=false 3.3 如果当前锁对象是偏向锁模式(mark最后三位是101) //此时是当前线程重入,在栈上占用了一个LockRecord,更新了LockRecord的objectRef,但是没有更新displaced header 3.3.1 如果当前锁对象的mark的thread等于当前线程、epoch与类的epoch一致且最后三位是101(即[thread|epoch|****|101]),标记success=true //类的偏向模式关闭,有可能是其他同类对象撤销偏向次数过多导致。这里仅仅是尝试将锁对象的mark更新为类的prototype_header,不care是否成功 //锁对象可能进入无锁状态,原有偏向线程信息丢失 3.3.2 如果当前锁对象的类关闭(类的prototype_header最后三位不是101) 3.3.2.1 尝试将当前锁对象的mark更新为所属类的mark,更新后应该是[unknown_hash|age|001] 3.3.2.1.1 更新成功,rebiased_lock_entry_count 计数加1 3.3.2.1.2 更新失败,什么都不做 3.3.2.2 标记success=false // 尝试直接将线程偏向为当前线程(有可能是重入线程,有可能是竞争线程),将epoch更新为类的epoch,锁模式101,更新失败会进入撤销流程 3.3.3 如果当前锁对象的epoch和类的epoch不一致(前置条件:类没有关闭偏向) 3.3.3.1 尝试将锁对象的mark更新为[current_thread|class_epoch|age|101] 3.3.3.1.1 更新成功,rebiased_lock_entry_count加1 3.3.3.1.2 更新失败,进入撤销流程 InterpreterRuntime::monitorenter 3.3.3.2 标记success=true // 可能是匿名偏向,也能是偏向其他线程。匿名偏向下可能存在多个线程同时竞争。 3.3.4 如果锁对象的mark的的thread和当前thread不一致(前置条件:类没有关闭偏向 && epoch没有过期) //这里有可能多个线程竞争修改,只能一个成功 3.3.4.1 如果是匿名偏向,尝试将锁对象的mark修改为[current_thread|epoch|age|101] 3.3.4.1.1 修改成功,anonymously_biased_lock_entry_count加1 3.3.4.1.1 修改失败,进入撤销流程 InterpreterRuntime::monitorenter 3.3.4.2 如果不是匿名偏向,进入撤销流程 InterpreterRuntime::monitorenter 3.3.4.3 标记success=true 3.4 如果锁对象不是偏向锁模式,标记字段success=false // 类关闭偏向模式但是锁对象没有关闭偏向模式,或者锁对象不是偏向模式[3.3.2.1尝试关闭锁对象的偏向模式] 3.5 如果 success=false //如果是因为类关闭偏向(锁对象mark状态只有最后三位101是明确的,其余不确定),考虑3.3.2.1的CAS可能失败,锁对象的mark其实是unknown的 //如果是因为锁对象不是偏向锁模式,即最后三位不是101 3.5.1 将LockRecord的displaced_header置为[original_*|orignal_*|001] //这里使用CAS,可能有非重入两个线程A,B分别从3.3.2.1执行到这一步,存在contention,还有可能是当前线程重入与其他线程存在contention 3.5.2 尝试修改锁对象的mark为LockRecord的地址 3.5.2.1 如果修改失败 // A_enter-B_cause_bulk_rebias-A_enter,会出现当前线程重入 3.5.2.1.1 如果是当前线程重入,把LockRecord的displaced_header置为NULL,表示重入 3.5.2.1.2 如果不是当前线程重入,进入撤销流程 InterpreterRuntime::monitorenter //修改成功相当于已经是轻量级锁了,考察3.1 和 3.5.1 和 3.5.2,即锁对象和LockRecord之间满足轻量级锁 3.5.2.2 如果修改成功,什么都不做 3.6 如果 success=true,正常执行
补充 3.5.2.1 出现的场景