Skip to content

Comments

Fix: Reduce thread blocking by double null check instead of synchronizing on Method object#521

Merged
lukaszlenart merged 2 commits intoorphan-oss:mainfrom
fanzhongwei:main
Dec 29, 2025
Merged

Fix: Reduce thread blocking by double null check instead of synchronizing on Method object#521
lukaszlenart merged 2 commits intoorphan-oss:mainfrom
fanzhongwei:main

Conversation

@fanzhongwei
Copy link
Contributor

PR Title

Fix: Reduce thread blocking by double null check instead of synchronizing on Method object

PR Description

Problem

We observed severe thread blocking issues in production: 980+ threads were blocked at OgnlRuntime.invokeMethod with the state BLOCKED (on object monitor), waiting to lock java.lang.reflect.Method objects.

# 980 threads blocked
"default-17571" #91915 prio=5 os_prio=0 tid=0x00007f7b7c05d800 nid=0x167f9 waiting for monitor entry [0x00007f757f772000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at org.apache.ibatis.ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:1151)
	- waiting to lock <0x00000004a55aaf48> (a java.lang.reflect.Method)
	at org.apache.ibatis.ognl.OgnlRuntime.getMethodValue(OgnlRuntime.java:2146)
	at org.apache.ibatis.ognl.ObjectPropertyAccessor.getPossibleProperty(ObjectPropertyAccessor.java:66)
	at org.apache.ibatis.ognl.ObjectPropertyAccessor.getProperty(ObjectPropertyAccessor.java:160)
	at org.apache.ibatis.ognl.OgnlRuntime.getProperty(OgnlRuntime.java:3356)
	...

# got method lock thread
"HSFBizProcessor-DEFAULT-6-thread-5179" #90650 daemon prio=10 os_prio=0 tid=0x00007f794c319000 nid=0x16214 waiting for monitor entry [0x00007f758366e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at org.apache.ibatis.ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:1151)
	- locked <0x00000004a55aaf48> (a java.lang.reflect.Method)
	at org.apache.ibatis.ognl.OgnlRuntime.getMethodValue(OgnlRuntime.java:2146)
	at org.apache.ibatis.ognl.ObjectPropertyAccessor.getPossibleProperty(ObjectPropertyAccessor.java:66)
	at org.apache.ibatis.ognl.ObjectPropertyAccessor.getProperty(ObjectPropertyAccessor.java:160)
	at org.apache.ibatis.ognl.OgnlRuntime.getProperty(OgnlRuntime.java:3356)
	...

Root cause:

  • Direct synchronization on Method objects leads to heavy thread contention — all threads competing to execute method access checks must wait for the same Method monitor, which severely degrades system concurrency and performance.

Solution

To resolve the thread blocking while maintaining cache correctness, we introduced a double null check pattern to avoid unnecessary synchronization on Method objects:

  1. First check the _methodAccessCache for existing cached values (no synchronization)
  2. Only enter the synchronized block if the cache is missing
  3. Re-check the cache inside the synchronized block to prevent duplicate initialization (thread-safe)
  4. Keep all original business logic for method access checks and cache operations — only optimize the synchronization trigger condition

Key Code Changes

  • Moved the synchronized (method) block inside a double null check for methodAccessCacheValue
  • Avoid synchronizing on Method objects when the cache is already hit
  • Preserved full compatibility with existing cache logic (_methodAccessCache/_methodPermCache)
  • Maintained the original logic for syncInvoke calculation and method access check

Expected Outcome

  • Significantly reduce thread blocking by eliminating unnecessary locks on Method objects
  • Improve concurrency performance of OGNL runtime without breaking existing logic
  • Ensure cache atomicity and thread safety via double null check
  • No breaking changes — fully compatible with existing usage

Additional Notes

  • This change follows the "double-checked locking" best practice for lazy initialization
  • No changes to public APIs or behavior — only optimizes internal concurrency
  • Tested with MyBatis 3.5.x (the version where the blocking was observed)

@lukaszlenart
Copy link
Collaborator

Great, thanks for the patch! I'm going to merge it now and cherry pick changes into 4.8.x branch as well

@lukaszlenart lukaszlenart merged commit 6495d2b into orphan-oss:main Dec 29, 2025
6 checks passed
lukaszlenart added a commit that referenced this pull request Dec 29, 2025
Cherry-pick of PR #521 adapted for Java 8 compatibility.
Implements double-null-check pattern for _methodAccessCache and
_methodPermCache to avoid synchronizing on Method objects when
cache values already exist.

Fixes thread contention issues where multiple threads would block
waiting to acquire locks on java.lang.reflect.Method objects even
when cache values were already present.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@lukaszlenart
Copy link
Collaborator

PR is ready #523

@fanzhongwei
Copy link
Contributor Author

Great, thanks for the patch! I'm going to merge it now and cherry pick changes into 4.8.x branch as well

Thank you very much for merging the patch and backporting it to the 4.8.x branch!

I appreciate your attention to getting this optimization out across relevant branches, and feel confident it will help reduce the thread contention issues we observed.

@lukaszlenart
Copy link
Collaborator

I meant 3.4.x branch - sorry for mixing things up :(

lukaszlenart added a commit that referenced this pull request Dec 29, 2025
)

Cherry-pick of PR #521 adapted for Java 8 compatibility.
Implements double-null-check pattern for _methodAccessCache and
_methodPermCache to avoid synchronizing on Method objects when
cache values already exist.

Fixes thread contention issues where multiple threads would block
waiting to acquire locks on java.lang.reflect.Method objects even
when cache values were already present.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
@lukaszlenart lukaszlenart added this to the 3.5.0 milestone Feb 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants