3

How multithreading behaves in case of static members? Like in case of Singleton Class, if I try to create instance in a static block and in the static method, I return the instance and two threads try to execute getInstance() at the same time..how will this behave internally as static are loaded only one time

public class SingleTonUsingStaticInitialization {

    private static SingleTonUsingStaticInitialization INSTANCE = null;

    static {
        try {
            INSTANCE = new SingleTonUsingStaticInitialization();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private SingleTonUsingStaticInitialization() {
    }

    public static SingleTonUsingStaticInitialization getInstance() {
        return INSTANCE;
    }
}

3 Answers3

1

This specific example?

Threadingwise it's fine. Style wise it's deplorable. Do NOT write catch blocks like that. It also means if an exception does occur (it can't here - your constructor is empty), your code will dump half of the info to system error, and then continue, with a null reference instance of an instance of Singleton - causing other code to spit out NullPointerExceptions (because code just keeps going, as you caught the exception instead of letting it happen). If you treat all exceptions in this fashion, a single error will cause hundreds of errors in your logs, all irrelevant except the first one.

Once you take care of this exception handling issue, you can make the variable final, and no longer assign null to it. While you're at it, make the whole class final. It effectively is already (as you only have a private constructor):

public final class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Single() {}

    public static Singleton getInstance() {
        return INSTANCE;
}

The reason this works out when 2 threads simultaneously invoke getInstance, is the classloader mechanism itself: The Classloader guarantees that any given class is never loaded more than once by the same loader, even if 2 threads would simultaneously require this (the classloader will synchronize/lock to avoid this situation), and the initialization process (the static block - which was needlessly convoluted, as the example above shows) is similarly guarded and cannot possibly occur twice.

That's the only freebie you get: For static methods as a general rule, all threads can just run the same method all simultaneously if they want to. And here they do - it's just that the initialization (which includes the ... = new Singleton(); part) is gated to occur only once.

NB: If you must do more complex things, make helper methods:

public final class Singleton {
    private static Singleton INSTANCE = create();

    private Singleton(Data d) {
        // do stuff with 'd'
    }

    private static Singleton create() {
        Data d;
        try {
            d = readStuffFromDataIncludedInMyJar();
        } catch (IOException e) {
            throw new Error("states.txt is corrupted", e);
        }
        return new Singleton(d);
    }
}

This:

  1. Keeps code simple - static initializers are a thing, but fairly exotic java.
  2. Makes your code easier to test.
  3. It's an internal file; if that is missing/broken, that's about as likely / as problematic as one of your class files having gone for a walk. An Error is warranted here. This cannot possibly occur unless you wrote a bug or messed up a build, and hard crashing with a clear exception telling you precisely what's wrong is exactly what you want to happen in that case, not for code to blindly continue in a state where half of your app is overwritten with gobbledygook due to a disk drive crash or what not. Best to just conclude everything's borked, say so, and stop running.
rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Brilliant points. But I'm confused about the second code snippet. I do not see how `create` returns the necessary instance of `Singleton` class. Am I missing something? And your comment says do complex stuff in the constructor. But aren't you already doing the complex stuff in your `create` method *rather* than the constructor? – Basil Bourque Feb 19 '21 at 06:04
  • The second snippet is incomplete stub code. I know this sounds harsh, but you should be familiar with this kind of code before jumping into multithreading. Multithreading is a very difficult concept and it is not possible to learn it if you don't understand the basics of programming. – Torben Feb 19 '21 at 06:23
  • Yes, my apologies, the code snippet was a bit rushed. I've fixed it up and fixed up some typos while I was there. – rzwitserloot Feb 19 '21 at 13:47
  • 1
    I’d use `ExceptionInInitializerError` instead of the unspecific `Error`. There’s no need to add an additional message (that does guesswork about the problem). The chained `IOException` does already provide all information. – Holger Mar 18 '21 at 13:50
1

You are safe, in the sense that getInstance will return the same instance to multiple threads. This is guaranteed by The JLS, which is the only place you should delegate your understanding to. Specifically, that chapter says:

For each class or interface C, there is a unique initialization lock LC

And goes on further to say that:

The procedure for initializing C is then as follows:

Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC

In plain english, only a single thread can initialize that static field. Period.

The release of that lock gives proper happens-before guarantees between the action in the static block and any thread that uses that static field. This is implied from the same chapter, from:

An implementation may optimize this procedure by eliding the lock acquisition in step 1 (and release in step 4/5) when it can determine that the initialization of the class has already completed, provided that, in terms of the memory model, all happens-before orderings that would exist if the lock were acquired, still exist when the optimization is performed

Or, again, in plain english, whatever happens in that static block will be visible to all reading threads.


To that end, you will have a proper tool to remove that static block, via a so called "constant dynamic". The infrastructure for it is already in place, but javac still does not use it. You can read more here about it. Some projects already use that - if you have the proper jdk, for example jacoco does it.

Eugene
  • 117,005
  • 15
  • 201
  • 306
0

See the fine points made in the Answer by rzwitserloot.

Here is similar code to the code seen there, but adapted to use an enum as your singleton. Many folks recommended an enum as the ideal way to define a singleton in Java.

Thread-safety is guaranteed because of the same class loader behavior discussed in the other Answer. The enum is loaded once, and only once, per class loader when the class first loads.

If you have multiple threads accessing the single object defined by this enum, the first thread to reach the point where this class is loaded will cause our enum object to be instantiated with its constructor method running. The other remaining threads will block on their attempt to access the enum object until the enum class finishes loading and its one and only named enum object finishes its construction. The JVM juggles all this contention automatically, with no further coding needed by us. All that behavior is guaranteed by the Java specifications.

package org.vaadin.example;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public enum AppContext
{
    INSTANCE;

    private String wording;

    AppContext ( )
    {
        try
        {
            readStuffFromDataIncludedInMyJar();
        }
        catch ( IOException e )
        {
            throw new Error( "Failed to load from text file. Message # 7a608ddf-8c5f-4f77-a9c9-5ab852fde5b1." , e );
        }
    }

    private void readStuffFromDataIncludedInMyJar ( ) throws IOException
    {
        Path path = Paths.get( "/Users/basilbourque/example.txt" );
        String read = Files.readAllLines( path ).get( 0 );
        this.wording = read;
        System.out.println( "read = " + read );
    }

    public static void main ( String[] args )
    {
        System.out.println( AppContext.INSTANCE.toString() );
    }
}

When run.

read = Wazzup?
INSTANCE
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • The advantage of using an `enum` for this, is that you get serialization behaviour that is appropriate for singletons, and you get this for free. However, there is a cost: `enum` makes no sense here. This code is a WTF, and requires a comment to explain why in the blazes one would use the enum. In defense of the pattern, it is somewhat well known. But, the crux is: Why are you serializing a singleton? Who even uses java's baked in serialization, despised even by its authors? It happens, yes, but it's rare, and this pattern should not be used unless that is important. – rzwitserloot Feb 19 '21 at 13:49
  • As such, I challenge the statement _Many folks recommended an enum as the ideal way to define a singleton in Java._ - Perhaps 'many folks' recommend it, but an appeal to the masses isn't worth a heck of a lot, if said masses haven't thought it through. This is not usually how you'd want to go about making singletons. – rzwitserloot Feb 19 '21 at 13:50
  • when you say that `JLS` guarantees that, you have to be specific. – Eugene Feb 19 '21 at 19:54