喪心病狂!面試官讓我介紹:?jiǎn)卫J降氖褂脠?chǎng)景及"12種"寫法
1. 什么是單例模式
單例模式(Singleton Pattern)是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。
這種模式涉及到一個(gè)單一的類,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建。這個(gè)類提供了一種訪問(wèn)其唯一的對(duì)象的方式,可以直接訪問(wèn),不需要實(shí)例化該類的對(duì)象。
保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。

2. 單例模式的優(yōu)點(diǎn)/缺點(diǎn)和使用場(chǎng)景
2.1 單例模式的優(yōu)點(diǎn)
提供了對(duì)唯一實(shí)例的受控訪問(wèn)
由于系統(tǒng)中內(nèi)存只存在一個(gè)對(duì)象,因此可以節(jié)約系統(tǒng)的的資源,對(duì)于一些頻繁的創(chuàng)建和銷毀的對(duì)象單例模式無(wú)疑可以提高系統(tǒng)的性能
單例模式可以允許可變的數(shù)目的實(shí)例,使用單例模式進(jìn)行擴(kuò)展,使用控制單利對(duì)象相似的方法獲取指定個(gè)數(shù)的實(shí)例,及解決了單利對(duì)象,共享過(guò)多,而有損性能的問(wèn)題
2.2 單例模式的缺點(diǎn)
由于單例模式,不是抽象的所以可擴(kuò)展性比較差
職責(zé)過(guò)重,在一定程度上違背了單一職責(zé)
濫用單例將帶來(lái)一些負(fù)面的問(wèn)題,如為了節(jié)省資源將數(shù)據(jù)庫(kù)連接池對(duì)象設(shè)計(jì)為單例模式,可能會(huì)導(dǎo)致共享連接池對(duì)象的程序過(guò)多未出而出現(xiàn)的連接池溢出,如果實(shí)例化對(duì)象長(zhǎng)時(shí)間不用系統(tǒng)就會(huì)被認(rèn)為垃圾對(duì)象被回收,這將導(dǎo)致對(duì)象狀態(tài)丟失
2.3 單例模式的使用場(chǎng)景
開發(fā)工具類庫(kù)中的很多工具類都應(yīng)用了單例模式,比例線程池、緩存、日志對(duì)象等,它們都只需要?jiǎng)?chuàng)建一個(gè)對(duì)象。
3. 單例模式的12種寫法
3.1 餓漢式(靜態(tài)變量)
public class Singleton {
private static Singleton instance = new Singletion();
private Singletion() {
}
public static Singleton getInstance() {
return instance;
}
}3.2 餓漢式(靜態(tài)常量)
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}3.3 餓漢式(靜態(tài)代碼塊)
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}上面三種寫法本質(zhì)上其實(shí)是一樣的,也是各類文章在介紹餓漢式時(shí)常用的方式。但使用靜態(tài)final的實(shí)例對(duì)象或者使用靜態(tài)代碼塊依舊不能解決在反序列化、反射、克隆時(shí)重新生成實(shí)例對(duì)象的問(wèn)題。
序列化:一是可以將一個(gè)單例的實(shí)例對(duì)象寫到磁盤,實(shí)現(xiàn)數(shù)據(jù)的持久化;二是實(shí)現(xiàn)對(duì)象數(shù)據(jù)的遠(yuǎn)程傳輸。
當(dāng)單例對(duì)象有必要實(shí)現(xiàn) Serializable 接口時(shí),即使將其構(gòu)造函數(shù)設(shè)為私有,在它反序列化時(shí)依然會(huì)通過(guò)特殊的途徑再創(chuàng)建類的一個(gè)新的實(shí)例,相當(dāng)于調(diào)用了該類的構(gòu)造函數(shù)有效地獲得了一個(gè)新實(shí)例!
3.4 懶漢式(線程不安全)
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}3.5 懶漢式(線程安全,存在同步開銷)
class Singleton {
private static Singleton intance = null;
private Singleton() {
//私有構(gòu)造函數(shù)
}
public static synchronized Singleton getInstance()
{
if (intance == null) {
intance = new Singleton();
}
return intance;
}
}3.6 懶漢式(線程假裝安全,同步代碼塊)
class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
// 4.加入同步代碼塊
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}3.7 DCL「雙重檢測(cè)鎖:Double Checked Lock」(假)
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}3.8 DCL「雙重檢測(cè)鎖:Double Checked Lock」 單例(真)
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}3.9 靜態(tài)內(nèi)部類(推薦使用)
public class Singleton {
private Singleton() {
}
private static class SingletonInstance()
{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}3.10 枚舉類單例模式
public enum Singleton {
INSTANCE;
private Resource instance;
Singleton() {
instance = new Resource();
}
public Resource getInstance() {
return instance;
}
public static class Resource {
private Resource() {
}
}
}3.11 登記式單例--使用Map容器來(lái)管理單例模式
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) {
return map.get(key);
}
}3.12 內(nèi)部枚舉類
public interface MySingleton {
void doSomething();
}
public enum Singleton implements MySingleton {
INSTANCE {
@Override
public void doSomething() {
System.out.println("complete singleton");
}
};
public static MySingleton getInstance() {
return Singleton.INSTANCE;
}
}4.Spring依賴注入對(duì)單例的使用
在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);
}4.1 doGetBean中g(shù)etSingleton
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);
throw ex;
}
});
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 new ScopeNotActiveException(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 new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}4.2 getSingleton的實(shí)現(xiàn)
返回在給定名稱下注冊(cè)的(原始)單例對(duì)象,檢查已經(jīng)實(shí)例化的單例并允許提前 對(duì)當(dāng)前創(chuàng)建的單例的引用(解析循環(huán)引用)。
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;
}作者:ZhangSan_Plus
鏈接:https://juejin.cn/post/7006124074338369567
來(lái)源:掘金
