Locked ownable synchronizers: - <0x000000008523ca00> (a java.util.concurrent.ThreadPoolExecutor$worker)
"taskExecutorForHb-197"#342 prio=5 os_prio=0 tid=0x0007f5d7c72f800 nid=0x182c waiting for monitor entry [0x00007f5ccd6d4000] java.lang.thread.State: BLOCKED (on object monitor) - waiting to lock <0x0000000080a772d8> (a java.util.concurrent.ConcurrentHashMap$Node) at org.apache.ibatis.reflection.DefaultReflection.DefaultReflectorFactory.fineForClass(DefaultReflectorFactory.java:1674)
是在源码的这个位置,DefaultReflectorFactory.java
public Reflector findForClass(Class<?> type){ if (classCacheEnabled) { // synchronized (type) removed see issue #461 return reflectorMap.computeIfAbsent(type, Reflector::new); } else { returnnew Reflector(type); } }
The entire method invocation is performed atomically, so the function is applied at most once per key. Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map.
可以看到,为了保证原子性,当对相同 key 进行修改时,可能造成线程阻塞。显而易见这会造成比较严重的性能问题,在 Java 官方 Jira,也有用户提到了同样的问题。[JDK-8161372] ConcurrentHashMap.computeIfAbsent(k,f) locks bin when k present很多开发者都以为 computeIfAbsent 是不会造成线程 block 的,但事实却是相反的。而 Java 官方当时认为这个方法的设计没问题。但反思之后也觉得,在性能还不错的 concurrenthashmap 中有这么个拉胯兄弟确实不太合适。所以,官方在 JDK9 中修复了这个问题。
publicclassMapUtil{ /** * A temporary workaround for Java 8 specific performance issue JDK-8161372 .<br> * This class should be removed once we drop Java 8 support. * * @see <a href="https://bugs.openjdk.java.net/browse/JDK-8161372">https://bugs.openjdk.java.net/browse/JDK-8161372</a> */ publicstatic <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction){ V value = map.get(key); if (value != null) { return value; } return map.computeIfAbsent(key, mappingFunction::apply); }