Skip to main content

Avoid Double-Checked Locking for Lazy Initialization

High
reliabilityperformanceconcurrency

What is it?

Double-checked locking is a pattern used to reduce synchronization overhead when initializing an object. It involves checking an object's state both before and after a synchronized block to determine initialization necessity.

Why apply it?

Double-checked locking, except for int and float, does not reliably ensure thread safety in a platform-independent way. It risks a thread using an uninitialized instance while another is still creating it. Modern JVMs optimize method synchronization, making this pattern unnecessary and potentially harmful.

How to fix it?

Use a synchronized method to ensure thread safety during initialization. Alternatively, employ a static inner class, which guarantees lazy and safe initialization by the JVM.

Examples

Example 1:

Negative

In the negative example, double-checked locking is used, which is not thread-safe.

public class ResourceFactory {
private static Resource resource;

public static Resource getInstance() {
if (resource == null) { // Noncompliant
synchronized (ResourceFactory.class) {
if (resource == null) {
resource = new Resource();
}
}
}
return resource;
}

private static class Resource { /* Resource class implementation */ }
}

Example 2:

Positive

In the positive example, thread safety is ensured by synchronizing the entire method.

public class ResourceFactory {
private static Resource resource;

public static synchronized Resource getInstance() { // Compliant
if (resource == null) {
resource = new Resource();
}
return resource;
}

private static class Resource { /* Resource class implementation */ }
}

Negative

In the negative example, the double-checked locking pattern results in potential race conditions.

public class ResourceFactory {
private static Resource resource;

public static Resource getInstance() {
if (resource == null) { // Noncompliant
synchronized (ResourceFactory.class) {
if (resource == null) {
resource = new Resource();
}
}
}
return resource;
}

private static class Resource { /* Resource class implementation */ }
}

Example 3:

Positive

In the positive example, a static inner class is used for lazy initialization, ensuring thread safety.

public class ResourceFactory {

private static class ResourceHolder {
public static final Resource resource = new Resource(); // Compliant
}

public static Resource getResource() {
return ResourceHolder.resource;
}

private static class Resource { /* Resource class implementation */ }
}