InExperience
InExperience

Reputation: 103

Singleton, threads and synchronization

I'm following the book Head First Design Patterns (CHAPTER 5, The Singleton Pattern).

They talk about the overlap of the thread into the method getInstance() when the synchronized keyword is not used.

How can I see, on the screen, the difference of the two behaviours of the threads?

  public class ChocolateBoiler {    

        private boolean empty;
        private boolean boiled;
        private static ChocolateBoiler chocolateBoilerInstance;

        public ChocolateBoiler() {
            empty = true;
            boiled = false;
        }

        public static (synchronized) ChocolateBoiler getInstance() {
            if (chocolateBoilerInstance == null) {
                chocolateBoilerInstance = new ChocolateBoiler();
            }
            return chocolateBoilerInstance;
        }

        public void fill() {
            if (isEmpty()) {
                empty = false;
                boiled = false;
                // fill the boiler with a milk/chocolate mixture
            }
        }

        public void drain() {
            if (!isEmpty() && isBoiled()) {
                // drain the boiled milk and chocolate
                empty = true;
            }
        }

        public void boil() {
            if (!isEmpty() && !isBoiled()) {
                // bring the contents to a boil
                boiled = true;
            }
        }

        public boolean isEmpty() {
            return empty;
        }

        public boolean isBoiled() {
            return boiled;
        }

        public static void main(String[] args) {

            ChocolateBoiler boiler = ChocolateBoiler.getInstance();
            boiler.fill();
            boiler.boil();
            boiler.drain();
        }
 }

Upvotes: 2

Views: 94

Answers (1)

mentallurg
mentallurg

Reputation: 5207

You example is too complex. I have created another, more simple, look the code below. If you run it as is, you will see lines in the console where 0 and 1 are mixed, like following:

11111111111111111111000000000000111111111111111111
11111111111111111111110000000000001111111111111111

The reason is that two threads modify the same instance variable "value" in the singleton simultaneously.

Now add word "synchronized" to both methods "setValue" and "printValue" and run it again. You will see, that all lines consist of 0 or 1 only. They are not mixed any more.

00000000000000000000000000000000000000000000000000
11111111111111111111111111111111111111111111111111

The reason is, that at any time only one of the threads modifies the variable, because "synchronized" prevents simultaneous access to the singleton object from different threads.

Here is the code:

public class Main {

    public static class Singleton {

        private static Singleton instance = new Singleton();

        public static Singleton getInstance() {
            return instance;
        }

        private char[] value = new char[50];

        private Singleton() {
        }

        public void printValue() {
            for (int i = 0; i < value.length; i++) {
                System.out.print(value[i]);
            }
            System.out.println();
        }

        public void setValue(String newValue) {
            for (int i = 0; i < newValue.length() && i < value.length; i++) {
                value[i] = newValue.charAt(i);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
        }

    }

    public static void main(String[] args) {
        final int MAX = 100000;

        Thread thread1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < MAX; i++) {
                    Singleton.getInstance().setValue("00000000000000000000000000000000000000000000000000");
                    yield();
                }
            }
        };

        Thread thread2 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < MAX; i++) {
                    Singleton.getInstance().setValue("11111111111111111111111111111111111111111111111111");
                    yield();
                }
            }
        };

        Thread thread3 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < MAX; i++) {
                    System.out.printf("%5d:   ", i);
                    Singleton.getInstance().printValue();
                    yield();
                }
            }
        };

        thread1.start();
        thread2.start();
        thread3.start();
    }

}

Upvotes: 3

Related Questions