diff --git a/src/main/java/org/springframework/data/convert/CustomConversions.java b/src/main/java/org/springframework/data/convert/CustomConversions.java index a6eba7ea8..f867f647a 100644 --- a/src/main/java/org/springframework/data/convert/CustomConversions.java +++ b/src/main/java/org/springframework/data/convert/CustomConversions.java @@ -497,7 +497,7 @@ public class CustomConversions { */ static class ConversionTargetsCache { - private Map, TargetTypes> customReadTargetTypes = new HashMap<>(); + private volatile Map, TargetTypes> customReadTargetTypes = new HashMap<>(); /** * Get or compute a target type given its {@code sourceType}. Returns a cached {@link Optional} if the value @@ -531,10 +531,19 @@ public class CustomConversions { if (targetTypes == null) { - Map, TargetTypes> customReadTargetTypes = new HashMap<>(this.customReadTargetTypes); - targetTypes = new TargetTypes(sourceType); - customReadTargetTypes.put(sourceType, targetTypes); - this.customReadTargetTypes = customReadTargetTypes; + synchronized (this) { + + TargetTypes customReadTarget = customReadTargetTypes.get(sourceType); + if (customReadTarget != null) { + targetTypes = customReadTarget; + } else { + + Map, TargetTypes> customReadTargetTypes = new HashMap<>(this.customReadTargetTypes); + targetTypes = new TargetTypes(sourceType); + customReadTargetTypes.put(sourceType, targetTypes); + this.customReadTargetTypes = customReadTargetTypes; + } + } } return targetTypes.computeIfAbsent(targetType, mappingFunction); @@ -554,7 +563,7 @@ public class CustomConversions { static class TargetTypes { private final Class sourceType; - private Map, Class> conversionTargets = new HashMap<>(); + private volatile Map, Class> conversionTargets = new HashMap<>(); TargetTypes(Class sourceType) { this.sourceType = sourceType; @@ -576,11 +585,19 @@ public class CustomConversions { if (optionalTarget == null) { - optionalTarget = mappingFunction.apply(new ConvertiblePair(sourceType, targetType)); + synchronized (this) { - Map, Class> conversionTargets = new HashMap<>(this.conversionTargets); - conversionTargets.put(targetType, optionalTarget == null ? Void.class : optionalTarget); - this.conversionTargets = conversionTargets; + Class conversionTarget = conversionTargets.get(targetType); + if (conversionTarget != null) { + optionalTarget = conversionTarget; + } else { + + optionalTarget = mappingFunction.apply(new ConvertiblePair(sourceType, targetType)); + Map, Class> conversionTargets = new HashMap<>(this.conversionTargets); + conversionTargets.put(targetType, optionalTarget == null ? Void.class : optionalTarget); + this.conversionTargets = conversionTargets; + } + } } return Void.class.equals(optionalTarget) ? null : optionalTarget; diff --git a/src/main/java/org/springframework/data/convert/SimplePropertyValueConversions.java b/src/main/java/org/springframework/data/convert/SimplePropertyValueConversions.java index f3b99c663..e6b8a1f19 100644 --- a/src/main/java/org/springframework/data/convert/SimplePropertyValueConversions.java +++ b/src/main/java/org/springframework/data/convert/SimplePropertyValueConversions.java @@ -53,7 +53,7 @@ public class SimplePropertyValueConversions implements PropertyValueConversions, private @Nullable ValueConverterRegistry valueConverterRegistry; - private Map, PropertyValueConverter> converterCache = new HashMap<>(); + private volatile Map, PropertyValueConverter> converterCache = new HashMap<>(); @SuppressWarnings("rawtypes") enum NoOpConverter implements PropertyValueConverter { @@ -171,17 +171,21 @@ public class SimplePropertyValueConversions implements PropertyValueConversions, if (converter == null) { - converter = requireConverterFactory().getConverter(property); + synchronized (this) { - Map, PropertyValueConverter> converterCache = new HashMap<>(this.converterCache); + PropertyValueConverter fromCache = converterCache.get(property); + if (fromCache != null) { + converter = fromCache; + } else { - if (converter == null) { - converterCache.put(property, NoOpConverter.INSTANCE); - } else { - converterCache.put(property, converter); + converter = requireConverterFactory().getConverter(property); + + Map, PropertyValueConverter> converterCache = new HashMap<>( + this.converterCache); + converterCache.put(property, converter != null ? converter : NoOpConverter.INSTANCE); + this.converterCache = converterCache; + } } - - this.converterCache = converterCache; } if (converter == NoOpConverter.INSTANCE) { diff --git a/src/main/java/org/springframework/data/mapping/InstanceCreatorMetadataSupport.java b/src/main/java/org/springframework/data/mapping/InstanceCreatorMetadataSupport.java index fcdf28056..6f0868f7b 100644 --- a/src/main/java/org/springframework/data/mapping/InstanceCreatorMetadataSupport.java +++ b/src/main/java/org/springframework/data/mapping/InstanceCreatorMetadataSupport.java @@ -35,7 +35,7 @@ class InstanceCreatorMetadataSupport> impleme private final Executable executable; private final List> parameters; - private Map, Boolean> isPropertyParameterCache = new HashMap<>(); + private volatile Map, Boolean> isPropertyParameterCache = new HashMap<>(); /** * Creates a new {@link InstanceCreatorMetadataSupport} from the given {@link Executable} and {@link Parameter}s. @@ -90,17 +90,25 @@ class InstanceCreatorMetadataSupport> impleme Boolean cached = isPropertyParameterCache.get(property); - if (cached != null) { - return cached; + if (cached == null) { + + synchronized (this) { + + Boolean fromCache = isPropertyParameterCache.get(property); + if (fromCache != null) { + cached = fromCache; + } else { + + cached = doGetIsCreatorParameter(property); + + Map, Boolean> isPropertyParameterCache = new HashMap<>(this.isPropertyParameterCache); + isPropertyParameterCache.put(property, cached); + this.isPropertyParameterCache = isPropertyParameterCache; + } + } } - boolean result = doGetIsCreatorParameter(property); - - Map, Boolean> isPropertyParameterCache = new HashMap<>(this.isPropertyParameterCache); - isPropertyParameterCache.put(property, result); - this.isPropertyParameterCache = isPropertyParameterCache; - - return result; + return cached; } @Override