Home > Enterprise >  Singelton pattern with multithread
Singelton pattern with multithread

Time:05-17

I read about sigelton pattern with multithreads and I found that is implemented use synchronized .

my question is can I use wait() notify() or notifyAll() instead synchronized ?? and if yes which better synchronized or wait() notifyAll() ???

CodePudding user response:

The methods wait and notify are only meaningful within a synchronized method/block.

Try this:

public static void main(String[] args) {

    Object monitor = new Object();

    try {
        monitor.wait();
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

It will result in:

Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.base/java.lang.Object.wait(Native Method)
    at java.base/java.lang.Object.wait(Object.java:328)

This is because the current thread does not own the monitor.

Now try this, and you're in business (although not a very useful business yet):

public static void main(String[] args) {

    Object monitor = new Object();

    synchronized (monitor) {
        try {
            monitor.wait();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The thread now waits indefinitely for the notify that will never come.

Now try this:

public static void main(String[] args) {

    Object monitor = new Object();

    Thread a = new Thread(() -> {
        try {
            System.out.printf("Thread %s Waiting for notification%n", Thread.currentThread().getId());
            synchronized (monitor) {
                monitor.wait();
            }
            System.out.printf("Thread %s Received notification%n", Thread.currentThread().getId());
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

    Thread b = new Thread(() -> {
        try {
            System.out.printf("Thread %s Sleeping three seconds%n", Thread.currentThread().getId());
            Thread.sleep(3000);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("Thread %s Sending notification%n", Thread.currentThread().getId());
        synchronized (monitor) {
            monitor.notify();
        }
    });

    a.start();
    b.start();
}

Thread a synchronizes on the monitor and waits for a notification. Thread b sleeps for three seconds, then synchronizes on the monitor and notifies.

Example output:

Thread 14 Waiting for notification
Thread 15 Sleeping three seconds
Thread 15 Sending notification
Thread 14 Received notification

The numbers are the thread ID's which I logged for clarity.

Now to your question about a singleton. There are several ways to enforce that there is only one instance of a class. Most commonly, a framework like Spring enforces single instances of its beans within the application context. This is an accept pragmatic approach. To enforce a singleton within a class loader, probably the best way is to use an enum because it avoids serialization issues, but it's often done with a private constructor and a static instance field. Note that this type of self-managed singletons is considered an anti-pattern by most.

A singleton is normally only unique within a class loader. If you are running on an application server there might be multiple class loaders so you have to be careful there about the context in which you want to be able to use this singleton. In such a situation it might be better to avoid the singleton and instead create a separate deployable service or repository to keep the data in.

If you do have a singleton and want to be able to access it in a thread-safe manner, you will have to synchronize all access to it. You don't necessarily need wait/notify for this. For example:

static long start = System.currentTimeMillis();

public static void main(String[] args) {

    Object monitor = new Object();

    Thread a = new Thread(() -> {
        sleep(1000);
        log("before synchronized block");
        synchronized (monitor) {
            log("entered synchronized block");
            log("done");
        }
    });

    Thread b = new Thread(() -> {
        log("before synchronized block");
        synchronized (monitor) {
            log("entered synchronized block");
            sleep(3000);
            log("done");
        }
    });

    a.start();
    b.start();
}

private static void sleep(int millis) {
    try {
        log("sleeping for "   millis);
        Thread.sleep(millis);
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private static void log(String message) {
    System.out.printf("At %s thread %s %s%n", (System.currentTimeMillis() - start), Thread.currentThread().getId(), message);
}

Example output:

At 2 thread 15 before synchronized block
At 14 thread 15 entered synchronized block
At 25 thread 14 sleeping for 1000
At 25 thread 15 sleeping for 3000
At 1038 thread 14 before synchronized block
At 3038 thread 15 done
At 3039 thread 14 entered synchronized block
At 3040 thread 14 done

Just by using synchronized already the access can be made exclusive. Only when a thread needs the results of another thread does wait/notify come into play.

CodePudding user response:

can I use wait() notify() or notifyAll() instead synchronized?

The answer is the same regardless of whether you are trying to implement a singleton or implement anything else.

No.

synchronized is a mechanism by which threads in a Java program can safely share variables. It provides two things:

  1. It prevents threads from interfering with each other when they access the shared variables.

  2. It ensures that updates to shared variables made by one thread will become visible to other threads in a predictable and timely way.

wait(), notify(), and notifyAll() are a mechanism by which one thread can notify other threads that it has changed shared variables in some particular way.

wait() or notify() or notifyAll() don't technically require the use of synchronized, but the Java language designers arbitrarily added that requirement as a reminder that, if you're going to notify another thread about something you did to shared variables, or even if you're only going to look at something that another thread did to them, you're going to need synchronized to safely access the variables.

  • Related