define
Ensure that there is only one instance of a class and that you instantiate it yourself and make it available to the entire system
UML diagrams
scenario
- Objects that need frequent instantiation and destruction;
- Stateful tool class objects
- Frequent access to databases or file objects;
- Ensure that a class has only one object. For example, an object consumes too many resources, accesses IO and databases, and requires global configuration
Several singleton patterns
1. Hungry
It is initialized when static is declared and before the object is retrieved
Advantages: Fast object acquisition, thread-safe (since the virtual machine is guaranteed to load only once, there is no concurrency when loading classes)
Disadvantages: memory consumption (if a class has a static method, the class will be loaded when the static method is called, and the singleton initialization is completed during the class loading, slowing down the speed)
public class EagerSingleton {
// create a singleton
// Initialization is done at class loading time, so class loading is slow, but object fetching is fast
private static EagerSingleton instance = new EagerSingleton();Static private member, initialized
private EagerSingleton(a)
{
// Private constructor
}
public static EagerSingleton getInstance(a) // Static, no synchronization (class is initialized when loading, no multithreading problems)
{
returninstance; }}Copy the code
2. Slacker style
Synchronized: ensures the uniqueness of singletons in multiple threads
Advantages: Singletons are instantiated only when they are used, saving resources to some extent
Disadvantages: add synchronized keyword, resulting in unnecessary synchronization overhead. Not recommended.
// lazy singleton mode
// Lazy, no instance is created during class loading, so class loading is fast, but object fetching is slow at runtime
private static LazySingleton intance = null;// Static private member, not initialized
private LazySingleton(a)
{
// Private constructor
}
public static synchronized LazySingleton getInstance(a) // Static, synchronous, public access point
{
if(intance == null)
{
intance = new LazySingleton();
}
returnintance; }}Copy the code
3. Double Check Lock (DCL) implementation singleton (one of the most used singleton implementations)
(Double lock is represented by two short calls)
Advantages: Thread safety is ensured, and the synchronization lock is not performed when getInstance is invoked after the singleton object is initialized, resulting in high resource utilization
Disadvantages: slower first load, occasional failures due to Java memory model, some drawbacks in high concurrency environments, but very rare.
Code examples:
public class SingletonKerriganD {
/** * singleton instance */
private volatile static SingletonKerriganD instance = null;Volatitle is used to avoid DCL invalidation
//DCL nulls instance twice
// The first level of judgment is mainly to avoid unnecessary synchronization
// The second judgment is to create the instance in case of null.
public static SingletonKerriganD getInstance(a) {
if (instance == null) {
synchronized (SingletonKerriganD.class) {
if (instance == null) {
instance = newSingletonKerriganD(); }}return instance;
}
private SingletonKerriganD(a)
{
// Private constructor}}Copy the code
What is the DCL failure problem?
If thread A executes to instance = new SingletonKerriganD(), it does roughly three things:
- Allocate memory to the instance
- Call the constructor to initialize the member field
- Point the instance object to the allocated memory space (sInstance is not null)
If the execution order is 1-3-2, then in multi-threaded mode, instance! =null, in this case, thread B directly fetched instance, using error, difficult to trace. JDK1.5 and later volatile address DCL failures (double locking failures)
4. Static inner class singleton pattern
The singleton is initialized when singletonholder.instance is called,
Advantages: Thread-safe, singleton uniqueness, and delay singleton instantiation
Disadvantages: It takes two classes to do this, and while no static inner Class object is created, its Class object is created anyway and belongs to the permanent generation.
(Overall, this is considered the best singleton.)
public class SingletonInner {
private static class SingletonHolder{
private final static SingletonInner instance=new SingletonInner();
}
public static SingletonInner getInstance(a){
return SingletonHolder.instance;
}
private SingletonInner(a)
{
// Private constructor}}Copy the code
How does this approach keep singletons and thread-safe?
When the getInstance method is called for the first time, it first reads SingletonHolder.instance, the inner SingletonHolder class is initialized; When this class is loaded and initialized, it initializes its static domain to create the instance of Singleton. Since this is a static domain, it is initialized only once when the virtual machine loads the class, and the virtual machine guarantees its thread-safety. The advantage of this pattern is that the getInstance method is not synchronized and only performs access to a domain, so delayed initialization does not add any access costs.
Can this approach avoid reflex invasion?
The answer is: no. Many of the advantages of the singleton pattern for static inner classes mentioned on the web are that “you cannot get inner class properties from an outer class by reflection. So this form is a good way to avoid reflection intrusion “, this is not true, reflection can be retrieved from the inner class properties (for more information about reflection see Java reflection full solution), intrusion singleton pattern is not at all, look at the following example:
The singleton class is as follows:
package eft.reflex;
public class Singleton {
private int a;
private Singleton(a){
a=123;
}
private static class SingletonHolder{
private final static Singleton instance=new Singleton();
}
public static Singleton getInstance(a){
return SingletonHolder.instance;
}
public int getTest(a){
returna; }}Copy the code
The intrusion and test code is as follows:
public static void main(String[] args) throws Exception {
// Get fInstance for instance of inner class SingletonHolder by reflection
Class cInner=Class.forName("eft.reflex.Singleton$SingletonHolder");
Field fInstance=cInner.getDeclaredField("instance");
// Remove the final modifier for this field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(fInstance, fInstance.getModifiers() & ~Modifier.FINAL);
// Prints a property of the singleton, which is then tampered with by reflection
System.out.println("a="+ Singleton.getInstance().getTest());
// Get the a attribute fieldA for this singleton
fInstance.setAccessible(true);
Field fieldA=Singleton.class.getDeclaredField("a");
// create a newInstance of newSingleton through the reflection Class constructor.
Constructor constructor=Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton newSingleton= (Singleton) constructor.newInstance();
// Let fInstance point to a new instance newSingleton, at which point our singleton has been changed!
fInstance.set(null,newSingleton);
// Set a new value for property A of the pirated singleton
fieldA.setAccessible(true);
fieldA.set(newSingleton,888);
// Test for successful intrusion
System.out.println("After intrusion by reflection: A ="+ Singleton.getInstance().getTest());
fieldA.set(newSingleton,777);
System.out.println("After intrusion by reflection: A ="+ Singleton.getInstance().getTest());
}
Copy the code
Output result:
A =123 After reflection intrusion: A =888 After reflection intrusion: A =777Copy the code
Note: To avoid redeclaring objects when deserialized, add the following methods:
private Object readResolve(a) throws ObjectStreamException{
return sInstance;
}
Copy the code
Why is that? Because when the JVM deserializes and “assembles” a new object from memory, the readResolve method is automatically called to return our specified object
5. Enumerate singletons
Advantages: Thread-safe, prevents deserialization
Disadvantages: Enumeration is relatively memory intensive
public enum SingletonEnum {
instance;
public void doThing(a){}}Copy the code
Simply singletonene.instance gets the desired INSTANCE.
How does this approach guarantee singletons?
First, we make it clear in enumerations that the constructor is limited to private, that the constructor is executed when we access an enumeration instance, and that each enumeration instance is of static final type, meaning that it can only be instantiated once. When the constructor is called, our singleton is instantiated. That is, because instances in enum are guaranteed to be instantiated only once, our INSTANCE is guaranteed to be instantiated once.
The bytecode file generated in the example above describes instance as follows:
. public static final eft.reflex.SingletonEnum instance; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM ...Copy the code
As you can see, the ACC_STATIC and ACC_FINAL modifiers are automatically generated
Why are enumerated types thread-safe?
An enumeration we define is loaded and initialized by the virtual machine the first time it is actually used, and this initialization is thread-safe. As we know, to solve the concurrency problem of singletons, the main solution is the thread safety problem in the initialization process. So, because of the above features of enumerations, singletons implemented by enumerations are inherently thread-safe.
Why are singletons with enumerated types more memory intensive?
Here we analyze from the point of view of bytecode and compare the way of static inner class to illustrate. First, let’s look at the bytecode generated by the singleton of static inner class:
Classfile /G:/demo/reflexDemo/out/production/reflexDemo/eft/reflex/SingletonInner.class
Last modified 2019-8-8; size 500 bytes
MD5 checksum c69eb5edd5eec02d87359065d8650f02
Compiled from "SingletonInner.java"
public class eft.reflex.SingletonInner
SourceFile: "SingletonInner.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#19 // java/lang/Object."
":()V
#2 = Methodref #5.#20 // eft/reflex/SingletonInner$SingletonHolder.access$000:()Left/reflex/SingletonInner;
#3 = Class #21 // eft/reflex/SingletonInner
#4 = Class #22 // java/lang/Object
#5 = Class #23 // eft/reflex/SingletonInner$SingletonHolder
#6 = Utf8 SingletonHolder
#7 = Utf8 InnerClasses
#8 = Utf8
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Left/reflex/SingletonInner;
#15 = Utf8 getInstance
#16 = Utf8 ()Left/reflex/SingletonInner;
#17 = Utf8 SourceFile
#18 = Utf8 SingletonInner.java
#19 = NameAndType #8:#9 // "
":()V
#20 = NameAndType #24:#16 // access$000:()Left/reflex/SingletonInner;
#21 = Utf8 eft/reflex/SingletonInner
#22 = Utf8 java/lang/Object
#23 = Utf8 eft/reflex/SingletonInner$SingletonHolder
#24 = Utf8 accessThe $000
{
public eft.reflex.SingletonInner();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
line 4: 4
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Left/reflex/SingletonInner;
public static eft.reflex.SingletonInner getInstance();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: invokestatic #2 // Method eft/reflex/SingletonInner$SingletonHolder.access$000:()Left/reflex/SingletonInner;
3: areturn
LineNumberTable:
line 9: 0
}
Copy the code
Look again at the bytecode generated by enumeration singletons:
Classfile /G:/demo/reflexDemo/out/production/reflexDemo/eft/reflex/SingletonEnum.class Last modified 2019-8-9; size 989 bytes MD5 checksum b97cfb98be4e5ce15fd85e934cc9a75c Compiled from "SingletonEnum.java" public final class eft.reflex.SingletonEnum extends java.lang.Enum<eft.reflex.SingletonEnum> Signature: #31 // Ljava/lang/Enum<Left/reflex/SingletonEnum; >; SourceFile: "SingletonEnum.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM Constant pool: #1 = Fieldref #4.#34 // eft/reflex/SingletonEnum.$VALUES:[Left/reflex/SingletonEnum;
#2 = Methodref #35.#36 // "[Left/reflex/SingletonEnum;".clone:()Ljava/lang/Object;
#3 = Class #14 // "[Left/reflex/SingletonEnum;"
#4 = Class #37 // eft/reflex/SingletonEnum
#5 = Methodref #10.#38 // java/lang/Enum.valueOf:(Ljava/lang/Class; Ljava/lang/String;) Ljava/lang/Enum;
#6 = Methodref #10.#39 // java/lang/Enum."
":(Ljava/lang/String; I)V
#7 = String #11 // instance
#8 = Methodref #4.#40 // eft/reflex/SingletonEnum."
":(Ljava/lang/String; I)V
#9 = Fieldref #4.#41 // eft/reflex/SingletonEnum.instance:Left/reflex/SingletonEnum;
#10 = Class #42 // java/lang/Enum
#11 = Utf8 instance
#12 = Utf8 Left/reflex/SingletonEnum;
#13 = Utf8 $VALUES
#14 = Utf8 [Left/reflex/SingletonEnum;
#15 = Utf8 values
#16 = Utf8 ()[Left/reflex/SingletonEnum;
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 valueOf
#20 = Utf8 (Ljava/lang/String;) Left/reflex/SingletonEnum;
#21 = Utf8 LocalVariableTable
#22 = Utf8 name
#23 = Utf8 Ljava/lang/String;
#24 = Utf8
#25 = Utf8 (Ljava/lang/String; I)V
#26 = Utf8 this
#27 = Utf8 Signature
#28 = Utf8 ()V
#29 = Utf8 doThing
#30 = Utf8
#31 = Utf8 Ljava/lang/Enum
;
#32 = Utf8 SourceFile
#33 = Utf8 SingletonEnum.java
#34 = NameAndType #13:#14 // $VALUES:[Left/reflex/SingletonEnum;
#35 = Class #14 // "[Left/reflex/SingletonEnum;"
#36 = NameAndType #43:#44 // clone:()Ljava/lang/Object;
#37 = Utf8 eft/reflex/SingletonEnum
#38 = NameAndType #19:#45 // valueOf:(Ljava/lang/Class; Ljava/lang/String;) Ljava/lang/Enum;
#39 = NameAndType #24:#25 // "
":(Ljava/lang/String; I)V
#40 = NameAndType #24:#25 // "
":(Ljava/lang/String; I)V
#41 = NameAndType #11:#12 // instance:Left/reflex/SingletonEnum;
#42 = Utf8 java/lang/Enum
#43 = Utf8 clone
#44 = Utf8 ()Ljava/lang/Object;
#45 = Utf8 (Ljava/lang/Class; Ljava/lang/String;) Ljava/lang/Enum;
{
public static final eft.reflex.SingletonEnum instance;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static eft.reflex.SingletonEnum[] values();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #1 // Field $VALUES:[Left/reflex/SingletonEnum;
3: invokevirtual #2 // Method "[Left/reflex/SingletonEnum;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Left/reflex/SingletonEnum;"
9: areturn
LineNumberTable:
line 3: 0
public static eft.reflex.SingletonEnum valueOf(java.lang.String);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc_w #4 // class eft/reflex/SingletonEnum
3: aload_0
4: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
7: checkcast #4 // class eft/reflex/SingletonEnum
10: areturn
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 name Ljava/lang/String;
public void doThing();
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this Left/reflex/SingletonEnum;
static {};
flags: ACC_STATIC
Code:
stack=4, locals=0, args_size=0
0: new #4 // class eft/reflex/SingletonEnum
3: dup
4: ldc #7 // String instance
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field instance:Left/reflex/SingletonEnum;
13: iconst_1
14: anewarray #4 // class eft/reflex/SingletonEnum
17: dup
18: iconst_0
19: getstatic #9 // Field instance:Left/reflex/SingletonEnum;
22: aastore
23: putstatic #1 // Field $VALUES:[Left/reflex/SingletonEnum;
26: return
LineNumberTable:
line 4: 0
line 3: 13
}
Copy the code
Static comparison: Enumeration class default inheritance java.lang.Enum number of Constant pools comparing two bytecodes, SingletonInner. Class 24, SingletonEnum. Class 45 comparing two bytecodes file size, Singletoninner.class 500 bytes, singletonene.class 989 bytes, nearly double the difference. We know that the JVM loads the constant pool of the class file into memory and stores it in the method area. Enumerations consume more memory (although this does not represent a difference in actual memory consumption), so update when more convincing evidence is available
Why doesn’t enumeration deserialization generate new instances?
Enumeration classes inherit java.lang.Enum(not java.lang.Object) by default.
/** * prevent default deserialization */
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData(a) throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
Copy the code
The problem with all singletons is that once the Serializable interface is implemented, they are no longer singletons because each call to the readObject() method returns a newly created object. One solution is to use the readResolve() method to prevent this from happening. However, in order to ensure that enumeration types are unique to the JVM, as stated in the Java specification, each enumeration type and the enumeration variables defined are unique to the JVM. Java makes special provisions on the serialization and deserialization of enumeration types. In serialization, Java simply prints the enumeration object’s name property into the result. In deserialization, Java finds the enumeration object by name using the valueOf method of java.lang.Enum. At the same time, the compiler does not allow any customization to this serialization mechanism, so the writeObject, readObject, readObjectNoData, writeReplace, and readResolve methods are disabled.
During the serialization process, if the writeObject and readObject methods are defined in the serialized class, the VM attempts to call the writeObject and readObject methods in the object class for user-defined serialization and deserialization. If there is no such method, the defaultWriteObject method of ObjectOutputStream and the defaultReadObject method of ObjectInputStream are called by default
Use containers to implement the singleton pattern
During program initialization, multiple singleton types are injected into a unified management class. When used, objects of corresponding types are obtained by key. In this way, we can manage multiple singleton types and operate through a unified interface when used. This approach takes advantage of the Map’s key uniqueness to ensure singletons.
public class SingletonManager {
private static Map<String,Object> map=new HashMap<String, Object>();
private SingletonManager(a){}
public static void registerService(String key,Object instance){
if (!map.containsKey(key)){
map.put(key,instance);
}
}
public static Object getService(String key){
returnmap.get(key); }}Copy the code
conclusion
All singleton patterns need to deal with the following problems:
- Privatize the constructor
- Obtain a unique instance through a static method
- Ensuring thread safety
- Prevent new instances caused by deserialization, etc.
Recommended uses: DCL, static inner classes, enumerations
Advantages of singleton mode
- Only one object, low memory cost, good performance (when the generation of an object requires more resources, such as reading configuration, generate other dependent objects, you can directly generate a singleton object when the application starts, let it permanently in memory to solve the problem)
- Avoid multiple resource usage (one write file operation, only one instance exists in memory, avoid simultaneous write operations on the same resource file)
- Set up global access points in the system to optimize and share resource access (e.g., design a singleton class that is responsible for mapping all data tables)
Disadvantages of the singleton pattern
- Generally no interface, difficult to expand
- On Android, a singleton holding a Context is prone to memory leaks. In this case, you need to note that the Context passed to the singleton should be an Application Context
Android source singleton pattern
Singleton mode is widely used, according to the actual business needs, here only leads to the source code of individual scenarios, no longer detailed explanation, interested readers can look at the source code
In normal Android development, we often use Context to get system services, Such as ActivityManagerService AccountManagerService system services, such as are actually ContextImpl SystemServiceRegistry. GetSystemService to obtain specific service, SystemServiceRegistry is a final class. Containers are used here to implement the singleton pattern
SystemServiceRegistry
final class SystemServiceRegistry {
private static finalHashMap<Class<? >, String> SYSTEM_SERVICE_NAMES =newHashMap<Class<? >, String>();private static finalHashMap<String, ServiceFetcher<? >> SYSTEM_SERVICE_FETCHERS =newHashMap<String, ServiceFetcher<? > > ();private SystemServiceRegistry(a) {}static {
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
new CachedServiceFetcher<ActivityManager>() {
@Override
public ActivityManager createService(ContextImpl ctx) {
return newActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler()); }}); . }public static Object getSystemService(ContextImpl ctx, String name) { ServiceFetcher<? > fetcher = SYSTEM_SERVICE_FETCHERS.get(name);returnfetcher ! =null ? fetcher.getService(ctx) : null;
}
private static <T> void registerService(String serviceName, Class<T> serviceClass, ServiceFetcher<T> serviceFetcher) { SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName); SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); }... }Copy the code
- Windows ManagerGlobal(lazy) in Windows Manager IMPL
public static WindowManagerGlobal getInstance(a) {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
returnsDefaultWindowManager; }}Copy the code
The resources
- Android source code design pattern analysis and combat