Singleton
class has Thread Safe Double Checked Locking
to make sure the instance should be initialized only once, but before java 1.5
, the Double Checked Locking
was failing. Java Compiler
tries to optimize the execution. Such as caching data on CUP cache
or reordering execution
. to understand better consider the below situation.
- The 1st execution cycle,
thread A
checks forObjects.isNull(appleInstance)
, currently, object is null so it getstrue
. - The 2nd execution cycle, the
Thread A processor
assigns the space inheap
memory for the object. - The 3rd execution cycle,
Thread A processor
creates areference
of the object in thestack
memory location and points to it. - The 4th execution cycle,
Thread B
checks, for instance, nullabilityObjects.isNull(appleInstance)
, it gets returns false henceThread B
will take the object, butThread A
has still not updated the value in the object so currently object is created but the value is not present. - The 5th cycle,
Thread A
assigns the value in the instance variable. but till nowThread A
has aninvalid
object.
The issue here is that Thread B has read the variable before Thread A was able to complete the writing, it is also called the happens-before relationship.
If Multiple write operations happen on a variable, to optimize the process, JVM
copies the value of the instance variable from the main memory
into the CPU cache
, read from it, updates it and once all the updates are done, then writes back into main memory
. meanwhile, another thread processed by another different CPU
or core
tried to read the variable.
package org.wesome.dsalgo.design.pattern.singleton;
import java.util.Objects;
public class Apple {
private static Apple appleInstance;
public Apple() {
System.out.println("Apple default constructor");
}
public static Apple getAppleInstance() {
System.out.println("Apple getAppleInstance");
if (Objects.isNull(appleInstance)) {
synchronized (Apple.class) {
if (Objects.isNull(appleInstance)) {
appleInstance = new Apple();
}
}
}
return appleInstance;
}
}
package org.wesome.dsalgo.design.pattern.singleton;
class AppleThread extends Thread {
public void run() {
Apple apple = Apple.getAppleInstance();
System.out.println("Object created by " + Thread.currentThread().getName() + " is " + apple);
}
}
public class SingletonClass {
public static void main(String[] args) {
AppleThread AppleThread1 = new AppleThread();
AppleThread AppleThread2 = new AppleThread();
AppleThread1.start();
AppleThread2.start();
}
}
plugins {
id 'java'
id "io.freefair.lombok" version "6.4.1"
}
group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}
test {
useJUnitPlatform()
}
in java 1.5
, a volatile
variable was introduced, and volatile
directors the compiler
to always read from the main memory and write into the main memory and not from the CPU cache.
it guarantees that another thread will not see the half initialized value of another thread ie the happens-before relationship
, the read will happen on the volatile
variable only when all the write has successfully completed.
After introduction of volatile
in java 1.5
, the Double Checked Locking
was not failing.
Double Locked Singleton Object creation is antipattern and should be avoided. Bill Pugh Singleton Solution or Holder Singleton Pattern is advised
CPU cache
was designed to improve performance while execution. forcing thread every time read and write into main memory
and bypassing the CPU cache
will be a performance hit.
package org.wesome.dsalgo.design.pattern.singleton;
import java.util.Objects;
public class Apple {
private volatile static Apple appleInstance;
public Apple() {
System.out.println("Apple default constructor");
}
public static Apple getAppleInstance() {
System.out.println("Apple getAppleInstance");
if (Objects.isNull(appleInstance)) {
synchronized (Apple.class) {
if (Objects.isNull(appleInstance)) {
appleInstance = new Apple();
}
}
}
return appleInstance;
}
}
package org.wesome.dsalgo.design.pattern.singleton;
class AppleThread extends Thread {
public void run() {
Apple apple = Apple.getAppleInstance();
System.out.println("Object created by " + Thread.currentThread().getName() + " is " + apple);
}
}
public class SingletonClass {
public static void main(String[] args) {
AppleThread AppleThread1 = new AppleThread();
AppleThread AppleThread2 = new AppleThread();
AppleThread1.start();
AppleThread2.start();
}
}
plugins {
id 'java'
id "io.freefair.lombok" version "6.4.1"
}
group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}
test {
useJUnitPlatform()
}