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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

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
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

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Beyond Sequential: Why Rust's Threading Model Changed How I Think About Concurrent Programming
  • Mastering Thread-Local Variables in Java: Explanation and Issues
  • Unlocking Performance: Exploring Java 21 Virtual Threads [Video]
  • Visualizing Thread-Safe Singletons in Java

Trending

  • Dropwizard vs. Micronaut: Unpacking the Best Framework for Microservices
  • Cosmos DB Disaster Recovery: Multi-Region Write Pitfalls and How to Evade Them
  • DGS GraphQL and Spring Boot
  • Building Resilient Identity Systems: Lessons from Securing Billions of Authentication Requests
  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.

By 
Niklas Schlimm user avatar
Niklas Schlimm
·
Mar. 26, 19 · Presentation
Likes (11)
Comment
Save
Tweet
Share
27.1K 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.

Related

  • Beyond Sequential: Why Rust's Threading Model Changed How I Think About Concurrent Programming
  • Mastering Thread-Local Variables in Java: Explanation and Issues
  • Unlocking Performance: Exploring Java 21 Virtual Threads [Video]
  • Visualizing Thread-Safe Singletons in Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!