Join Elisabeth Robson for an in-depth discussion in this video Dealing with multithreading, part of Foundations of Programming: Design Patterns.
Now that you've seen how to implement Singleton, we should cover some issues that can crop up when using it, as well as how we deal with those issues. To understand these issues, let's return to the Singleton code where, in the getInstance method, we've checked to see if there's already a unique instance. And if so, we return it. If there's not, we then create a new instance and return it. Now, we've been to this code a bit, and it may seem fairly bulletproof, like it always does the right thing, but, there's a case where it might not, and that case is where we're using multiple threads. Let's step through the execution of two threads, Thread one and Thread two.
Here's the hypothetical, but also very possible, path of execution through this code by these two threads. First, thread one checks if unique is null. And if it is, it enters the block to create the new Singleton object. About this time, thread two get's it's turn. And it also checks unique for null. Which it is. And so it enters the block to create a new instance of Singleton. Thread one gets to run again, and it creates a new Singleton, assigns it to unique, and returns it. Thread two then runs, creates another Singleton object, assigns it to unique, overwriting the original reference to the Singleton and returns that.
So, we have two callers that now have references to two different Singletons. That's a problem. So what do we do? Well, here is some ideas. We can make sure that getInstance method is synchronized in some way, so the two threads can't get near the state of execution. Or, perhaps we could create a Singleton and not do it the way we are here with lazy instantiation. Let's step through some code to see how we might fix this.
- What are design patterns?
- Encapsulating code that varies with the strategy pattern
- Setting behavior dynamically
- Implementing the observer pattern
- Creating chaos with inheritance
- Extending behavior with composition
- Dealing with multithreading and the singleton pattern
- Revising the design for a state machine
- Encapsulating iteration with the collection pattern
- Encapsulating object creation with the factory method pattern