Understanding Software Design Patterns

Lately I have been quite busy with work and have not had time to write posts for this blog. Today I woke up shortly after 04:00 AM. Typically I go back to sleep until my alarm goes off, but today I had the desire to get up and so some on-line reading. I found the article Understanding Software Design Patterns by Bryant Son. The article deals with three design patterns:  Singleton, Factory and Observer. Given that I have generated at least one post Singleton Threads using the Singleton pattern, I spent time reading the article and experimenting with it. I will cover the last two patterns in a different post.

The article does not show the Singleton pattern in action. I will show it in action with and without parallel access. Parallel access can be illustrated by running threads.

Let’s start by listing the Java code from the Eclipse IDE for the testing scaffolding using the Solution class:

package threads.singleton.canessa;

/*
 * 
 */
public class Solution {

	/*
	 * Test scaffolding.
	 */
	public static void main(String[] args) {
		
		final int LOOPS = 7;
		
		// **** loop starting threads ****
		for (int i = 0; i < LOOPS; i++) {
			
			// **** create first child thread ****
			ChildThread ct1 = new ChildThread();
			
			// **** create second child thread ****
			ChildThread ct2 = new ChildThread();
			
			// **** create third child thread ****
			ChildThread ct3 = new ChildThread();
			
			// **** start first child thread ****
			ct1.start();
			
			// **** start second child thread ****
			ct2.start();
			
			// **** start third child thread ****
			ct3.start();			
		}
		
		// **** delay for a few ****
		try {
			Thread.sleep(100);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		System.out.println();
		
		// **** create fourth child thread ****
		ChildThread ct4 = new ChildThread();
		
		// **** start fourth child thread ****
		ct4.start();
		
		// **** wait for fourth child thread to exit ****
		while (!ct4.isAlive()) {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
}

We define a loop that instantiates (but not start) three child threads. We then start each of the child threads. We should start a total of 3 * 7 = 21 threads.

Once that is done, we delay for a few milliseconds.

Finally we instantiate a fourth child thread, start it and wait for it to die.

Following is the source code for the ChildThread class.

package threads.singleton.canessa;

/*
 * 
 */
public class ChildThread extends Thread {

	@Override
	public void run() {
		
		// **** instantiate our Singleton class ****
		Singleton s = Singleton.getInstance();
		
		// **** increment the variable in the class ****
		s.val += 1;
		
		// **** display the updated value ****
		System.out.println("val: " + s.val);
	}
}

The thread first instantiates a Singleton object. It then increments a public variable named val. Finally it displays the value.

If all is well, we should see the values increment by one. Given that there are 21 threads plus one, we should see the value increment until it reaches 22.

Now let’s take a look at the Singleton class:

package threads.singleton.canessa;


import java.util.concurrent.Semaphore;


/*
 * Typical Singleton implementation
 */
public class Singleton {
	
	// **** members ****
	private	static	volatile 	Semaphore	sem 			= new Semaphore(1);

	private static 	volatile 	Singleton 	singleInstance 	= null;

	public 						int 		val 			= 0;
	
	// **** constructor ****
	private Singleton() {
		val = 0;
	}
	
	// **** get instance of this class ****
	public static Singleton getInstance() {
				
//		// **** request access ****
//		try {
//			sem.acquire();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
		
		// **** instantiate this class (if needed) ****
		if (singleInstance == null) {
			
			// **** instantiate Singleton ****
			singleInstance = new Singleton();
			
			// **** inform user what is going on ****
			System.out.println("singleInstance was null hashCode: " + singleInstance.hashCode());
		}
		
//		// **** release access ****
//		sem.release();

		// **** return this class ****
		return singleInstance;
	}

}

Note that we have commented out the use of the semaphore which will be used to grant access to the instance of the Singleton object returned by the getInstance() method.

Also note that when the code instantiates a new Singleton, we display a message indicating the hash code of the singleInstance object. As described in the article we should encounter issues.

Following is a screen capture of the console output executing the test scaffolding:

val: 8
val: 15
val: 10
singleInstance was null hashCode: 461918734
val: 9
val: 16
val: 9
val: 14
singleInstance was null hashCode: 461918734
val: 12
val: 8
val: 20
val: 21
val: 8
val: 13
val: 10
val: 11
val: 19
val: 18
val: 8
val: 8
val: 9
val: 17

val: 22

For starters the values are not incremented monotonically from 1 to 22. That said, we have 22 output statements for the value. The reason that the output is incorrect is that the CPU is faster that the output so the values are displayed out of order and some are missing. This is interesting but not the point we want to understand and avoid.

We expect a single initialization of the singleInstance value. We got two initialization calls rendering the Singleton pattern incorrect. If you ran the code multiple times you should see from one to three messages indicating that the Singleton pattern returned one, two or three different instances.

Now let’s un comment the semaphore as illustrated by the following code:

package threads.singleton.canessa;


import java.util.concurrent.Semaphore;


/*
 * Typical Singleton implementation
 */
public class Singleton {
	
	// **** members ****
	private	static	volatile 	Semaphore	sem 			= new Semaphore(1);

	private static 	volatile 	Singleton 	singleInstance 	= null;

	public 						int 		val 			= 0;
	
	// **** constructor ****
	private Singleton() {
		val = 0;
	}
	
	// **** get instance of this class ****
	public static Singleton getInstance() {
				
		// **** request access ****
		try {
			sem.acquire();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		// **** instantiate this class (if needed) ****
		if (singleInstance == null) {
			
			// **** instantiate Singleton ****
			singleInstance = new Singleton();
			
			// **** inform user what is going on ****
			System.out.println("singleInstance was null hashCode: " + singleInstance.hashCode());
		}
		
		// **** release access ****
		sem.release();

		// **** return this class ****
		return singleInstance;
	}

}

Before we access the singleInstance variable, we need to get access to the semaphore. Note that the semaphore was declared for single access. Once the singleInstance variable has been updated (if needed) we release the semaphore and return the singleInstance.

Output from the console follows:

singleInstance was null hashCode: 1903557031
val: 1
val: 2
val: 4
val: 3
val: 6
val: 7
val: 8
val: 10
val: 5
val: 12
val: 14
val: 11
val: 16
val: 18
val: 9
val: 19
val: 17
val: 15
val: 14
val: 20
val: 21

val: 22

As one can see, the program displays 22 times the value and the values are not in perfect order. This is expected due to the way the solution was coded. What is important is the fact that the singleInstance variable is initialized just once. This is the idea behind the Singleton pattern.

The take away is that when working in a multi threaded environment you must always make use of an access control mechanism (e.g., semaphore) when implementing a Singleton.

The entire code for this project can be found in my GitHub repository.

If you have comments or questions regarding this or any other post in this blog, or if you would like me to help with any phase in the SDLC (Software Development Life Cycle) of a product or service, please do not hesitate and leave me a note below. Requests for help will remain private.

Keep on reading and experimenting. It is the best way to learn and refresh your knowledge!

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.