Guys, starting today, we’re going to talk about design patterns.
preface
Someone said, why use design pattern, I don’t need it, can still achieve business. Yes, you’re right, it is. However, it is important to know that good design patterns enable programs to be more abstract, reusable, and extensible, and that the code can make minimal changes to the original logic when business requirements change.
For example, the strategy mode is to abstract out a parent class and inherit different logic with different subclasses. If new logic branches need to be added, only a subclass needs to be extended without modifying the parent class and other subclasses.
As Meyer mentioned, one of the most important principles in software design is the on/off principle.
Open for extensions, closed for modifications.
Another example is the adapter pattern. Suppose there is an old system ten years ago, now also need to join the popular micro service registry to manage, time is tight, heavy task, how to do. At this point, you can use the design idea of the adapter to package a layer in the way of microservices and adapt it. It does not affect the business logic of the old system, and can realize the new management mode.
In coding, the application of design pattern reflects the good culture of a programmer. Currently, there are 23 accepted design patterns, which we’ll talk about later.
The singleton pattern
Today, let’s start with the simplest singleton pattern. The singleton pattern means that only one instance is allowed in the system. How do you guarantee that?
public class Singleton01 {
private static Singleton01 sg = new Singleton01();
private Singleton01() {
}
public static Singleton01 getInstance() {
return sg;
}
public static void main(String[] args) {
int i=0;
while(i<100){
new Thread(()->{
System.out.println(getInstance());
}).start();
i++;
}
}
}
Copy the code
We first define a private constructor to prevent other programs from new it. Why is that? A new one, B new one, of course, is not A singleton.
It also provides a static public method, public static Singleton01 getInstance (), that other programs can call to get an instance of the class. Static () {static (); static () {static () {static ();
Sg objects are static, so Singleton01 performs new Singleton01() when loading and initializing sg objects.
In the getInstance() method, we directly return the unique SG object created when the class was loaded.
This is called the hanky-hank approach to the singleton pattern, which means you can’t wait to create an instance first.
So let’s see if this is a good way to write it, if this is actually a singleton. When I run it (the main method is not shown in the following code to avoid duplication, but the purpose is to print the memory address of the object in a multi-threaded environment to verify that it is the same object), I see that the output is the same address, no problem.
Singleton.Singleton01@1e60980
Singleton.Singleton01@1e60980
Singleton.Singleton01@1e60980
Singleton.Singleton01@1e60980
Singleton.Singleton01@1e60980
.
Copy the code
The problem with hangry
Someone said, you load this object as soon as it comes up, in case I don’t use it at all, isn’t that a waste of memory?
So let’s write it a different way, so when we call getInstance, we call it null and then we create the object. Yeah, that’s the lazy style. It’s not in a hurry.
LanHanShi
1.
public class Singleton02 {
private static Singleton02 sg;
private Singleton02() {
}
public static Singleton02 getInstance() {
if(sg==null){
dosth();
sg = new Singleton02();
}
return sg;
}
// Simulate time-consuming logic
private static void dosth() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Copy the code
Singleton.Singleton02@172603c
Singleton.Singleton02@172603c
Singleton.Singleton02@1e60980
Singleton.Singleton02@1406116
.
Copy the code
You can obviously see that the memory address is different, that is, multiple Singleton02 objects are generated, indicating that this is not correct.
So what’s the problem? Is dosth? Looks like it. Just get rid of him. No, we add doSTH here (omitted to avoid duplication) to simulate the process of creating an object.
Let’s analyze why.
Thread 1 and thread 2 come up, they both decide the object is empty, they both go and create the object.
So, the problem is, in a multi-threaded environment, concurrent access, no locking.
2.
public class Singleton03 {
private static Singleton03 sg;
private Singleton03() {
}
public synchronized static Singleton03 getInstance() {
if(sg==null){
dosth();
sg = new Singleton03();
}
return sg;
}
}
Copy the code
Singleton.Singleton03@173340f
Singleton.Singleton03@173340f
Singleton.Singleton03@173340f
Singleton.Singleton03@173340f
Singleton.Singleton03@173340f
.
Copy the code
Now, the code is fine. It’s a singleton. But adding the synchronized keyword directly to the method declaration is efficient because every time the method is called to fetch an object, the lock must be applied even if the object already exists.
3.
In order to improve the efficiency of the above writing method, we improve and narrow the scope of synchronized. Added judgment, if empty, need to create the object before the lock.
public class Singleton04 {
private static Singleton04 sg;
private Singleton04() {
}
public static Singleton04 getInstance() {
if(sg==null){
synchronized(Singleton04.class){
dosth();
sg = new Singleton04();
}
}
return sg;
}
}
Copy the code
Singleton.Singleton04@afc10b
Singleton.Singleton04@afc10b
Singleton.Singleton04@14c637e
Singleton.Singleton04@14c637e
Singleton.Singleton04@df4aed
Singleton.Singleton04@12c6b2b
Copy the code
From the running results, there is a problem, what is the problem, let’s analyze.
Suppose two threads access the getInstance method at the same time, thread 1 and thread 2 both determine that SG is empty at time 1, thread 1 takes the lock at time 2, creates the object, returns, and releases the lock. Thread 2 then takes the lock at time 3 and creates the object again. At this point, two instances are created in the system.
So what to do? So let’s do a little bit of an improvement and say, again, after locking, if the instance is empty.
4.
public class Singleton05 {
private static volatile Singleton05 sg;
private Singleton05() {
}
public static Singleton05 getInstance() {
if(sg==null){
synchronized(Singleton05.class){
if(sg==null){
dosth();
sg = new Singleton05();
}
}
}
return sg;
}
}
Copy the code
Singleton.Singleton05@142188f
Singleton.Singleton05@142188f
Singleton.Singleton05@142188f
Singleton.Singleton05@142188f
Singleton.Singleton05@142188f
.
Copy the code
This method is called a DCL (Double Check Lock), and determines whether two times are empty. In fact, the first determination can intercept most requests, if the object has been created, no subsequent locking process. Compared to the above method, the efficiency will be greatly improved.
And there’s another point that I don’t know if you noticed. Yes, the keyword volatile. As we all know, volatile serves two important purposes: thread visibility and instruction reordering.
The sg object declaration is volatile to prevent instruction reordering by the JVM.
In simple terms, it prevents an object from being returned without being fully initialized. I won’t expand on that, but we’ll talk about that later when we talk about the JVM.
Static inner class
Let’s look at one last way of writing a static inner class.
public class Singleton06 {
private Singleton06() {
}
private static class Singleton06_Inner{
private static Singleton06 sg = new Singleton06();
}
public static Singleton06 getInstance() {
return Singleton06_Inner.sg;
}
}
Copy the code
Singleton.Singleton06@3c0755
Singleton.Singleton06@3c0755
Singleton.Singleton06@3c0755
Singleton.Singleton06@3c0755
Copy the code
We still make the constructor private, so other classes can’t create objects. Here we declare an inner class Singleton06_Inner, which can call the constructor of the outer class to create the object.
So, what’s the difference between the hungry and the hungry?
We say that an inner class is loaded only when a property or method is called, whereas a virtual machine loads a class only once. This is guaranteed by the virtual machine itself.
So, this approach can be lazy and singleton.
Usage scenarios
The singleton pattern is generally used for resource sharing, such as database connection pools, thread pools, caches, log objects, classes that read configuration files, and so on.
Spring also uses singleton patterns, such as beans by default.
conclusion
Today we talked about the singleton pattern, which ensures that only one instance of a class can be created in the system. It mainly talks about three ways to create a singleton, hanhan-style (class initialization creates the object), lazy (DCL) and static inner class.
Hanky-hank is relatively simple, but may waste system resources; Lazy is a bit more complex, need to pay attention to multithreaded access processing; Static inner classes are simple and secure, and recommended.
Well, that’s all for this sharing and I’ll see you in the next design pattern.
Finally, if you feel useful, write ok, welcome to forward, like, thank you.
I am Leng Feng, focus on the field of technology development, pay attention to me, let us grow together.