diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java index 41ec12464ddc..8982f23ade35 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java @@ -33,6 +33,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public abstract class BaseRuntimeElementDefinition { @@ -40,7 +41,7 @@ public abstract class BaseRuntimeElementDefinition { private final Class myImplementingClass; private final String myName; private final boolean myStandardType; - private Map, Constructor> myConstructors = Collections.synchronizedMap(new HashMap<>()); + private final Map, Constructor> myConstructors = new ConcurrentHashMap<>(); private List myExtensions = new ArrayList<>(); private List myExtensionsModifier = new ArrayList<>(); private List myExtensionsNonModifier = new ArrayList<>(); @@ -84,27 +85,24 @@ private Constructor getConstructor(@Nullable Object theArgument) { argumentType = theArgument.getClass(); } - Constructor retVal = myConstructors.get(argumentType); - if (retVal == null) { + Constructor retVal = myConstructors.computeIfAbsent(argumentType, type -> { for (Constructor next : getImplementingClass().getConstructors()) { - if (argumentType == VOID_CLASS) { + if (type == VOID_CLASS) { if (next.getParameterTypes().length == 0) { - retVal = (Constructor) next; - break; - } - } else if (next.getParameterTypes().length == 1) { - if (next.getParameterTypes()[0].isAssignableFrom(argumentType)) { - retVal = (Constructor) next; - break; + return (Constructor) next; } + } else if (next.getParameterTypes().length == 1 && next.getParameterTypes()[0].isAssignableFrom(type)) { + return (Constructor) next; } } - if (retVal == null) { - throw new ConfigurationException(Msg.code(1695) + "Class " + getImplementingClass() - + " has no constructor with a single argument of type " + argumentType); - } - myConstructors.put(argumentType, retVal); + return null; + }); + + if (retVal == null) { + throw new ConfigurationException(Msg.code(1695) + "Class " + getImplementingClass() + + " has no constructor with a single argument of type " + argumentType); } + return retVal; } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6323-concurrent-constructor-access.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6323-concurrent-constructor-access.yaml new file mode 100644 index 000000000000..51b32ae0b6b9 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6323-concurrent-constructor-access.yaml @@ -0,0 +1,5 @@ +--- +type: perf +issue: 6323 +title: "A synchronization choke point was removed from the model object initialization code, reducing the risk of +multi-thread contention."