1. What is the singleton pattern
The Singleton Pattern is one of the simplest design patterns in Java. This type of design pattern is the creation pattern, which provides the best way to create objects.
This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created. This class provides a way to access its unique objects directly, without instantiating the objects of the class.
Ensure that a class has only one instance and provide a global access point to access it.
2. Advantages/disadvantages of singleton mode and usage scenarios
2.1 Advantages of singleton mode
- Provides controlled access to a unique instance
- Since only one object exists in the system memory, it can save system resources, and the singleton pattern can provide system performance for some objects that are frequently created and destroyed
- Singleton mode can allow a variable number of instances, the use of singleton mode for extension, the use of control singleton object similar method to obtain a specified number of instances, and solve the problem of singleton object, sharing too much, and loss of performance
2.2 Disadvantages of the singleton pattern
- Because the singleton pattern is not abstract, it has poor scalability
- The responsibility is too heavy, and to some extent it violates the single responsibility
- Abuse singleton will bring some negative problems, such as in order to save resources will object database connection pool design for the singleton pattern, may lead to a Shared connection pool object program did not out too much and the connection pool leak, if instantiation objects for a long time it will be not system is considered garbage objects are recycled, and this will lead to object state is lost
2.3 Application Scenarios of singleton Mode
Many of the utility classes in the development utility library apply the singleton pattern, proportional thread pools, caches, log objects, and so on, all requiring the creation of a single object.
3. 12 ways of writing singleton pattern
3.1 Hungry (static variable)
public class Singleton {
private static Singleton instance = new Singletion();
private Singletion(a) {}public static Singleton getInstance(a) {
returninstance; }}Copy the code
3.2 Hungry (static constant)
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(a) {}public static Singleton getInstance(a) {
returnINSTANCE; }}Copy the code
3.3 Hungry (Static Code Block)
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton(a) {}public static Singleton getInstance(a) {
returninstance; }}Copy the code
The above three ways of writing are essentially the same, and are often used in various articles to introduce hungry Han style. However, using statically final instance objects or using static code blocks still does not solve the problem of regenerating instance objects during deserialization, reflection, or cloning. Serialization: One is to write a singleton instance object to disk to realize data persistence; The second is to realize the remote transmission of object data. When a singleton is necessary to implement the Serializable interface, even if its constructor is made private, a new instance of the class will be created in a special way when it is deserialized, effectively obtaining a new instance by calling the constructor of the class!
3.4 Lazy (Threads are Unsafe)
public class Singleton {
private static Singleton instance;
private Singleton(a) {}public static Singleton getInstance(a) {
if (instance == null) {
instance = new Singleton();
}
returninstance; }}Copy the code
3.5 Lazy (thread-safe with synchronization overhead)
class Singleton {
private static Singleton intance = null;
private Singleton(a) {
// Private constructor
}
public static synchronized Singleton getInstance(a)
{
if (intance == null) {
intance = new Singleton();
}
returnintance; }}Copy the code
3.6 Lazy (threads pretend to be safe and synchronize code blocks)
class Singleton {
private static Singleton singleton;
private Singleton(a) {}public static Singleton getInstance(a) {
if (singleton == null) {
// 4. Add synchronization code block
synchronized (Singleton.class) {
singleton = newSingleton(); }}returnsingleton; }}Copy the code
3.7 DCL “Double Checked Lock :Double Checked Lock” (false)
public class Singleton {
private static Singleton instance;
private Singleton(a) {}public static Singleton getInstance(a) {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = newSingleton(); }}}returninstance; }}Copy the code
3.8 DCL “Double Checked Lock :Double Checked Lock” singleton (true)
public class Singleton {
private static volatile Singleton instance;
private Singleton(a) {}public static Singleton getInstance(a) {
if (instance == null) {
synchronized (Singleton.class) {
instance = newSingleton(); }}returninstance; }}Copy the code
3.9 Static Inner Classes (Recommended)
public class Singleton {
private Singleton(a) {}private static class SingletonInstance(a)
{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(a) {
returnSingletonInstance.INSTANCE; }}Copy the code
3.10 Enumerating class singleton patterns
public enum Singleton {
INSTANCE;
private Resource instance;
Singleton() {
instance = new Resource();
}
public Resource getInstance(a) {
return instance;
}
public static class Resource {
private Resource(a) {}}}Copy the code
3.11 Registry Singletons — Use a Map container to manage singletons
public class Singleton {
private static Map<String, Object> map = new HashMap<>();
public static void reglisterService(String key, Object instance) {
if (!map.containsKey) {
map.put(key, instance);
}
}
public static Object getInstance(String key) {
returnmap.get(key); }}Copy the code
3.12 Internal enumeration classes
public interface MySingleton {
void doSomething(a);
}
public enum Singleton implements MySingleton {
INSTANCE {
@Override
public void doSomething(a) {
System.out.println("complete singleton"); }};public static MySingleton getInstance(a) {
returnSingleton.INSTANCE; }}Copy the code
4.Spring dependency injection’s use of singletons
In AbstractBeanFactory
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null.null.false);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null.false);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false);
}
Copy the code
4.1 doGetBean中getSingleton
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if(sharedInstance ! =null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if(args ! =null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if(requiredType ! =null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return(T) parentBeanFactory.getBean(nameToLookup); }}if(! typeCheckOnly) { markBeanAsCreated(beanName); } StartupStep beanCreation =this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if(requiredType ! =null) {
beanCreation.tag("beanType", requiredType::toString);
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if(dependsOn ! =null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex); }}}// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
if(! StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally{ afterPrototypeCreation(beanName); }}); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); }catch (IllegalStateException ex) {
throw newScopeNotActiveException(beanName, scopeName, ex); }}}catch (BeansException ex) {
beanCreation.tag("exception", ex.getClass().toString());
beanCreation.tag("message", String.valueOf(ex.getMessage()));
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
finally{ beanCreation.end(); }}// Check if required type matches the type of the actual bean instance.
if(requiredType ! =null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}
Copy the code
4.2 Implementation of getSingleton
Returns the (original) singleton registered under the given name, checks the singleton that has been instantiated and allows advance references to the currently created singleton (resolution loop references).
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) { ObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
if(singletonFactory ! =null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
Copy the code