DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Approaching Volatile and Synchronized With Singleton Examples

Approaching Volatile and Synchronized With Singleton Examples

Learn more about using volatile and synchronized keywords with these singleton examples.

Niklas Schlimm user avatar by
Niklas Schlimm
CORE ·
Mar. 26, 19 · Presentation
Like (11)
Save
Tweet
Share
22.77K Views

Join the DZone community and get the full member experience.

Join For Free

Now, there is still lots of discussion around using volatile and synchronized. The wholevolatile subject isn't very new, but to understand, it is still fundamentally important for every dedicated Java developer. Also, I could not find any comprehensive summaries on the subject that were both meaningful and selective examples, so this motivated me to invest some time into writing this post for you.  

Simply put, the volatile keyword marks a variable that always goes to the main memory, for both reads and writes, of the multiple threads accessing it (visibility feature). On the other hand, synchronized will cause all modifications guarded by the considered lock to synchronize with main memory (visibility feature) and adds mutual exclusion; this prevents an object from being seen in an inconsistent state by one thread while some other thread is updating that object.

Singleton Using Volatile Bean Pattern

Let's understand this using a lazy loaded singleton that uses the double-checked locking idiom and implements the "volatile bean pattern." Here is the first snippet:

public class MutableSingleton {
    private static volatile MutableSingleton INSTANCE;
    private static final Object mutex = new Object();
    private volatile boolean someFlag;
    // more mutable state on this singleton

    private MutableSingleton(boolean someFlag) {
        this.someFlag = someFlag;
    }

    public static MutableSingleton getInstance() {
        MutableSingleton singleton = INSTANCE;
        if (singleton != null)
            return singleton;
        synchronized (mutex) {
            if (INSTANCE == null)
                INSTANCE = new MutableSingleton(false);
            return INSTANCE;
        }
    }

    public boolean isSomeFlag() {
        return someFlag;
    }

    public void setSomeFlag(boolean someFlag) {
        this.someFlag = someFlag;
    }
}


That's a mutable thread-safe singleton where the INSTANCE field is set to volatile, and someFlag is also volatile but access to someFlag is not synchronized. Changes made to the singleton should immediately be visible to other threads (visibility feature).  Here is a test case to find out:

public class MutableSingletonTest {

    private long counter = 0;

    public Object[] execute(Object... arguments) {
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                MutableSingleton.getInstance().setSomeFlag(true);
                System.out.println("Timer interrupted main thread ...");
                timer.cancel();
            }
        }, 1000);
        while (!MutableSingleton.getInstance().isSomeFlag()) {
            counter++;
        };
        System.out.println("Main thread was interrupted by timer ...");
        return new Object[] { counter, MutableSingleton.getInstance().isSomeFlag() };
    }

    private class Worker implements Runnable {
        @Override
        public void run() {
            Object[] result = execute();
            System.out.println(result[0]+"/"+result[1]);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MutableSingletonTest volatileExample = new MutableSingletonTest();
        Thread thread1 = new Thread(volatileExample.new Worker(), "Worker-1");
        thread1.start();
        Thread.sleep(5000);
    }
}


Let's go through this quickly. The main() method (line 29) creates thread1 that runs the  execute() method (line 24). The execute() method (line 5) schedules another task for execution, which will cause a TimerThread to be created (line 7). thread1 now simply iterates around (line 14) until someFlag is set to true by the timer thread (line 9). When you execute this example with the  MutableSingleton, the result should look like this:

Timer interrupted main thread ...
Main thread was interrupted by timer ...
804404197/true


The timer thread and thread1 synchronize properly, causing the volatile keyword to be applied to the  MutableSingleton.  

Making That Singleton Fail

Delete the volatile keywords on the  MutableSingleton and run the test above again! The result should be like this (at least it was on my machine), which means thread coordination fails now:

Timer interrupted main thread ...


This suggests that changes to someFlag are not visible to thread1 anymore. And that's, indeed, correct. The timer thread set someFlag to true, but since the  volatile keyword was deleted,  thread1 cannot see any updates happening. 

Singleton Without Volatile? 

Now, let's do some other tests. What happens if we erase the volatile keywords on the  MutableSingleton but make the accessors to someFlag  synchronized? Will the changes to the singleton state be visible? Here is such a changed MutableSingleton class:

public class MutableSingletonSynchronized {
    private static MutableSingletonSynchronized INSTANCE;
    private static final Object mutex = new Object();
    private boolean someFlag;
    // more mutable state on this singleton

    private MutableSingletonSynchronized(boolean someFlag) {
        this.someFlag = someFlag;
    }

    public static MutableSingletonSynchronized getInstance() {
        MutableSingletonSynchronized singleton = INSTANCE;
        if (singleton != null)
            return singleton;
        synchronized (mutex) {
            if (INSTANCE == null)
                INSTANCE = new MutableSingletonSynchronized(false);
            return INSTANCE;
        }
    }

    public synchronized boolean isSomeFlag() {
        return someFlag;
    }

    public synchronized void setSomeFlag(boolean someFlag) {
        this.someFlag = someFlag;
    }

}


In the snippet, access to someFlag is synchronized and I have deleted the volatile keywords. Now, the result of the test case is like in the following console output, and notice this time without volatile:

Timer interrupted main thread ...
Main thread was interrupted by timer ...
60113040/true


The threads seem to coordinate correctly. The reason for that is that the  synchronized blocks around the accessors to someFlag also synchronize the thread local memory with main memory. Now, this raises the question: is volatile actually required if we use synchronized accessors?

The answer is an unambiguous: it depends.

A Non-Intuative Volatile Feature 

volatile on the singletons INSTANCE field is recommended whenever the singleton contains mutable state and is created using the double-checked locking idiom. So, in our example above, the answer is: although thread coordination worked fine without volatile, it should be applied. The reason for that is that volatile adds another "feature" here, sometimes referred to as: one-time safe publication. This feature of  volatile solves an infamous issue of the double-checked locking idiom. The root cause for this issue is the following fact:

In the absence of synchronization, it is possible to see an up-to-date value for an object reference that was written by another thread and still see stale values for that object's state.

What does that mean? Without volatile, there is a chance that the JIT compiler changes the assignment to theINSTANCE field (line 17 in the MutableSingleton examples). The JIT compiler may reorder the assigment, e.g. first sets the static INSTANCE field to an instance with default values, and  afterwards, it sets the mutable member fields (here someFlag) to the values initially passed to the constructor. So, with some unlucky timing, subsequent threads may enter the first null check (outside synchronisation in line 13 of the  MutableSingleton examples), which is not null, and then, they will return that incomplete default instance to their clients. For that complex, bothersome reason, the rule comes down to: 

You should use volatile on the double-checked locking idiom when creating mutable shared singletons. 

Because:

An assignment to the singleton INSTANCE field won't be reordered when you make that field volatile.

Immutable Objects

Now, with immutable singletons, it's a bit different. If the singleton is an immutable object, then lazy initialization with double-checked locking should work without having to use volatile fields. The idea is that reading and writing references to immutable objects are atomic. Therefore, with sensible care, and the sunshiny hint that you have to find out if your referenced object is really immutable:

With immutable objects applying volatile is not mandatory in the double-checked locking idiom.

Volatile Has No Mutual Exclusion

There is an addition to make which is quite important to understand when you deal with volatile. While volatile makes the garantee that all changes to the volatile variables are visible to all threads, it does not add the mutual exclusion feature, like synchronized does. This means that this code example is an thread-unsafe use of volatile: 

private static volatile int nextSerialNumber = 0;

public static int generateNextSerialNumber() {
   return nextSerailNumber++;
}


The change to nextSerialNumber will be visible, but since the (++) operator is actually performing two operations (read and write), there may be data races when multiple threads access the generateNextSerialNumber()  method and access that same memory location. The snippet can be fixed by adding synchronized like so:

private static int nextSerialNumber = 0;

public static synchronized int generateNextSerialNumber() {
   return nextSerailNumber++;
}

 

volatileis not required here anymore, as long the read accessor will also use synchronized.

Combining Volatile and Synchronized

After all that has been said, a complete thread-safe, well-performing singleton must look like the following example:

public class MutableSingletonComplete {
    private static volatile MutableSingletonComplete INSTANCE;
    private static final Object mutex = new Object();
    private volatile boolean someFlag;
    private volatile int counter;
    // more mutable state on this singleton
    private MutableSingletonComplete(boolean someFlag) {
        this.someFlag = someFlag;
    }
    public static MutableSingletonComplete getInstance() {
        MutableSingletonComplete singleton = INSTANCE;
        if (singleton != null)
            return singleton;
        synchronized (mutex) {
            if (INSTANCE == null)
                INSTANCE = new MutableSingletonComplete(false);
            return INSTANCE;
        }
    }
    public boolean isSomeFlag() {
        return someFlag;
    }
    public void setSomeFlag(boolean someFlag) {
        this.someFlag = someFlag;
    }
    public int getCounter() {
        return counter;
    }
    public synchronized void incrementCounter() {
        counter++;
    }
}


To summarize, for this:

  • You need volatile on the INSTANCE field (apply one-time publication feature).

  • You need volatile on the member fields of the singleton itself (apply visibility feature).

  • If you do more then an atomic action on mutable state, you will need to use synchronized, even though the field is declared volatile (apply mutual exclusion feature).

  • To make this complete: if the  volatile member field in your singleton happens to be an object reference, either the object implements the same thread-safe features like I just described, or it needs to be (at least "effectively") immutable.

OK, that's really enough confusion today for me. Let's go get some coffein. The code of the examples can be found here.

I hope the examples helped a little to further systemize the volatile subject. Let's discuss this if you would like to find out if I missed something, which can happen quickly on such complex topics. Looking forward to your comments, questions, and thoughts!

Threading

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Build a Spring Boot GraalVM Image
  • Real-Time Analytics for IoT
  • Introduction to Container Orchestration
  • Master Spring Boot 3 With GraalVM Native Image

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: