In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the knowledge of "how to use singleton mode". In the operation of actual cases, many people will encounter such a dilemma. Next, let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
A single example of static variable realization-- hungry man
Ensuring an instance is very simple, as long as the same instance is returned each time. The key is how to ensure the thread safety of the instantiation process.
Let's review the initialization of the class first.
JVM performs class loading before the class is instantiated.
The last step of class loading is to initialize the class. at this stage, the class constructor method is executed, and its main job is to initialize the static variables in the class, the code block.
The () method is blocked. In a multithreaded environment, if multiple threads initialize a class at the same time, only one thread will execute the class's (), and all other threads will be blocked. In other words, methods are given thread-safe capabilities.
Combined with the singleton we are going to implement, it is easy to think that we can create this singleton in the form of static variables, which is thread-safe, so we have come up with the first singleton implementation:
Private static Singleton singleton = new Singleton ()
Public static Singleton getSingleton () {
Return singleton
}
Quite simply, the only singleton is implemented through static variables and is thread-safe.
A seemingly perfect method also has its drawbacks, that is, it is possible that the class is loaded before I call the getSingleton method, such as reflection or other static variable static methods in the class. So the disadvantage of this method is that it may lead to a waste of resources. I instantiated the singleton when I didn't use it.
Under the same class loader, a type is initialized only once, and there are six times when class initialization can be triggered:
1. When the virtual machine starts, initialize the main class containing the main method; 2, new and other instructions to create an object instance 3, access the instruction of the static method or static field 4, the initialization process of the subclass if it is found that the parent class has not been initialized, 5, when the reflection API is used for the reflection call, 6, when the java.lang.invoke.MethodHandle instance is called for the first time
I don't care whether you use it or not, as long as my class is initialized, I will instantiate this singleton, which is compared to the hungry method. (I'm really hungry. Instantiate it first and leave it. When you want to eat, you can eat it directly.)
The disadvantage is that it may lead to a waste of resources (in the end, if the meal is not eaten, it will be wasted)
But in fact, this pattern is generally sufficient, because this class is usually used only when this instance is used, and there are few times when you need to use this class but do not use its singleton.
Of course, the words cannot be uttered, and there are better ways to solve this possible waste of resources.
Before we do that, let's take a look at the hungry implementation of Kotlin.
Kotlin hungry man-the simplest single case of object Singleton
是 吗? Yeah, it's gone.
This involves a keyword unique to kotlin: object (object).
There are three main uses of object:
Object expression
It is mainly used to create an object that inherits from some (or some) type of anonymous class.
Window.addMouseListener (object: MouseAdapter () {
Override fun mouseClicked (e: MouseEvent) {/ *. * /}
Override fun mouseEntered (e: MouseEvent) {/ *. * /}
})
Object declaration
It is mainly used for single case. That's the usage we use today.
Object Singleton
We can see the decompiled java code through the Show Kotlin Bytecode function of Android Studio:
Public final class Singleton {
Public static final Singleton INSTANCE
Private Singleton () {
}
Static {
Singleton var0 = new Singleton ()
INSTANCE = var0
}
}
Obviously, similar to the hungry guy we wrote in the previous section, the singleton is instantiated at the initialization stage of the class, except that one is through a static code block and the other is through a static variable.
Associated object
Object declarations within a class can be marked with the companion keyword, a bit like static variables, but not really static variables.
Class MyClass {
Companion object Factory {
Fun create (): MyClass = MyClass ()
}
}
/ / use
MyClass.create ()
Decompiled into Java code:
Public final class MyClass {
Public static final MyClass.Factory Factory = new MyClass.Factory ((DefaultConstructorMarker) null)
Public static final class Factory {
@ NotNull
Public final MyClass create () {
Return new MyClass ()
}
Private Factory () {
}
/ / $FF: synthetic method
Public Factory (DefaultConstructorMarker $constructor_marker) {
This ()
}
}
}
The principle is still a static inner class, and the method of the static inner class is finally called, but the name of the static inner class is omitted.
To implement a true static member, you need to modify the variable @ JvmField.
Optimize the hungry and cook when you eat-- the most elegant single example
Back to the point, since the hungry have shortcomings, let's find a way to solve it. Is there any way not to waste this example? That is, to achieve an on-demand loading order?
This involves another point of knowledge, the loading timing of static inner classes.
I just mentioned that when the class is loaded, the initialization process will only load static variables and code blocks, so static inner classes will not be loaded.
Static inner classes are loaded deferred, meaning that they are loaded only when they are explicitly used. Does not load when only external classes are used.
Based on this information, we can optimize the hungry mode to static inner class mode (java and kotlin versions):
Private static class SingletonHolder {
Private static Singleton INSTANCE = new Singleton ()
}
Public static Singleton getSingleton () {
Return SingletonHolder.INSTANCE
}
Companion object {
Val instance = SingletonHolder.holder
}
Private object SingletonHolder {
Val holder = SingletonDemo ()
}
Thread safety is also ensured through the class initialization () method, and on top of this, the singleton instantiation process is moved backward to the static inner class. So the static inner class is initialized when the getSingleton method is called, that is, the static singleton is instantiated.
As a whole, this method is perfect. Is it? It seems to have some disadvantages, for example, what if I want to pass in parameters when I call the getSingleton method to create an instance?
Yes, but you need to set the parameter values in the first place, and you cannot set the parameters dynamically by calling the getSingleton method. For example, write like this:
Private static class SingletonHolder {
Private static String test= "123"
Private static Singleton INSTANCE = new Singleton (test)
}
Public static Singleton getSingleton () {
SingletonHolder.test= "12345"
Return SingletonHolder.INSTANCE
}
In the end, the instantiated test will only be 123, not 12345. Because as soon as you start using SingletonHolder inner classes, the singleton INSTANCE will initially complete the instantiation, even if you assign a test, it will be after the singleton instantiation.
This is the disadvantage of static inner class methods. If you don't have to pass parameters dynamically, then this method is sufficient.
A single case that can pass parameters-- lazy man.
What if you need to pass parameters?
Then write normally, that is, when you call the getSingleton method, you can determine whether the singleton already exists or instantiate it if it does not exist.
Private static Singleton singleton
Public static Singleton getSingleton () {
If (singleton = = null) {
Singleton = new Singleton ()
}
Return singleton
}
I can see this very clearly and create an example only when I need it. In this way, I can ensure that I cook when I need to eat, which is a more regular way, but in the mind of the hungry, I will feel that this person is so lazy that I don't even prepare the meal first.
So this method is called lazy style.
However, the disadvantage of this method is also obvious, that is, it is not thread-safe, and different threads accessing getSingleton methods at the same time may lead to object instantiation errors.
So, lock it.
A lazy man with double check
How to add the lock is also a problem.
First of all, it is certain that the lock we add must be a class lock, because it is locked against this class to ensure that only one thread instantiates a singleton operation at a time.
Then there are two additions to class locks, decorating static methods and decorating class objects:
/ / method 1, modify the static method
Public synchronized static Singleton getSingleton () {
If (singleton = = null) {
Singleton = new Singleton ()
}
Return singleton
}
/ / method 2, code block decorates the class object
Public static Singleton getSingleton () {
If (singleton = = null) {
Synchronized (Singleton.class) {
If (singleton = = null) {
Singleton = new Singleton ()
}
}
}
Return singleton
}
Method 2 this way is what we often call the double check mode.
The difference between the two methods is that in this double check, we first judge whether the singleton is empty, and then enter the locking phase if it is empty, and then go to the instantiation code of the singleton.
So why are you doing this?
The first judgment is for performance. After the singleton has been instantiated, we don't need to enter the locking phase when we get the value again, so the first judgment is to reduce locking. Lock only in the process of the first instantiation, and then you can get the singleton directly. The second judgment is to prevent repeated creation of objects. When two threads come to synchronized at the same time, thread An acquires the lock and enters the creation object. The lock is released after the object is created, and thread B acquires the lock. If it is not determined whether the singleton is empty at this time, the object will be created again, repeating the operation.
At this point, it seems that all the problems have been solved.
Wait, is there really nothing wrong with the instantiation process of new Singleton ()?
In JVM, there is an operation called instruction rearrangement:
In order to optimize instructions and improve program efficiency, JVM will reorder instructions without affecting the execution results of single-threaded programs, but this reordering will not affect single-threaded programs.
To put it simply, some instructions may be disrupted without affecting the final result.
Let's take a look at the three main steps of the instructions in object instantiation:
1. Allocate object memory space 2, initialize object 3, and instance point to the memory address just allocated.
If we rearrange the second and third steps, the result will not affect:
1. Allocate object memory space 2. Instance points to the newly allocated memory address 3. Initialize object.
In this case, there is a problem:
When thread An enters the instantiation phase, new Singleton (), it has just finished the second step of allocating the memory address. At this time, thread B calls the getSingleton () method, goes to the first null, finds that it is not empty, and returns a singleton. There is a problem when the result is used, and the object is not initialized.
This is the problem that can be caused by instruction rearrangement.
So, we need to prohibit the rearrangement of instructions and the appearance of volatile.
Volatile has two main features:
Visibility. That is, the write operation is visible to other threads. Rearrangement of instructions is prohibited.
So add volatile to modify the variables, and the singleton pattern of double check is complete.
Private volatile static Singleton singleton; kotlin version double check / / without parameters
Class Singleton private constructor () {
Companion object {
Val instance: Singleton by lazy (mode = LazyThreadSafetyMode.SYNCHRONIZED) {
Singleton ()}
}
}
/ / with parameters
Class Singleton private constructor (privateval context: Context) {
Companion object {
@ Volatile private var instance: Singleton? = null
Fun getInstance (context: Context) =
Instance?: synchronized (this) {
Instance: Singleton (context). Apply {
Instance = this
}
}
}
}
Huh? Isn't it too easy to write without parameters? Volatile is gone, too? Are you sure it's okay?
No problem, the secret is in this delay attribute lazy (mode = LazyThreadSafetyMode.SYNCHRONIZED). Let's go in and have a look:
Public actual fun lazy (mode: LazyThreadSafetyMode, initializer: ()-> T): Lazy =
When (mode) {
LazyThreadSafetyMode.SYNCHRONIZED-> SynchronizedLazyImpl (initializer)
LazyThreadSafetyMode.PUBLICATION-> SafePublicationLazyImpl (initializer)
LazyThreadSafetyMode.NONE-> UnsafeLazyImpl (initializer)
}
Private class SynchronizedLazyImpl (initializer: ()-> T, lock: Any? = null): Lazy, Serializable {
Private var initializer: (()-> T)? = initializer
@ Volatile private var _ value: Any? = UNINITIALIZED_VALUE
/ / final field is required to enable safe publication of constructed instance
Privateval lock = lock?: this
Overrideval value: T
Get () {
Val _ v1 = _ value
If (_ v1! = = UNINITIALIZED_VALUE) {
@ Suppress ("UNCHECKED_CAST")
Return _ v1 as T
}
Return synchronized (lock) {
Val _ v2 = _ value
If (_ v2! = = UNINITIALIZED_VALUE) {
@ Suppress ("UNCHECKED_CAST") (_ v2 as T)
} else {
Val typedValue = initialization! ()
_ value = typedValue
Initializer = null
TypedValue
}
}
}
See, in fact, Volatile + synchronized double check is still used internally.
That's all for "how to use the singleton pattern". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.