1. 单例模式特点
构造方法私有化
实例化的变量引用私有化;
获取实例的方法共有
2. 单例模式应用场景
整个程序的运行中只允许有一个类的实例;
需要频繁实例化然后销毁的对象。
创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
方便资源相互通信的环境
3. Java中单例模式N种写法
这种方式最简单,缺点是类加载时就会实例化对象
1) 饿汉式
/**
* 饿汉式
*/
public class EaterSingleton {
private static final EaterSingleton INSTANCE = new EaterSingleton();
private EaterSingleton() {
}
public static EaterSingleton getInstance() {
return INSTANCE;
}
}
2). 懒汉模式-双重验证
线程安全,延迟初始化。这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
/**
* 懒汉双重验证
*/
public class DoubleCheckSingleton {
private volatile static DoubleCheckSingleton INSTANCE = null;
private DoubleCheckSingleton() {
}
public static DoubleCheckSingleton getInstance() {
if (INSTANCE == null) {
synchronized (DoubleCheckSingleton.class) {
if (INSTANCE == null) {
INSTANCE = new DoubleCheckSingleton();
}
}
}
return INSTANCE;
}
}
3). 静态内部类-单例模式
JVM保证单例,加载外部类时不会加载内部类,这样可以实现懒加载
package com.bytearch.designPattern.singleton;
/**
* 内部类
* 加载外部类时不会加载内部类
*/
public class AnonymousInnerClsSingleton {
private AnonymousInnerClsSingleton() {
}
private static class LazyHolder {
private final static AnonymousInnerClsSingleton INSTANCE = new AnonymousInnerClsSingleton();
}
public static AnonymousInnerClsSingleton getInstance() {
return LazyHolder.INSTANCE;
}
}
4). 枚举单例模式
不仅可以解决线程同步,还可以防止反序列化。枚举单例模式是《Effective Java》中推荐的
package com.bytearch.designPattern.singleton;
/**
* 内部枚举类单例模式
*/
public class Singleton {
private Singleton() {
}
/**
* 静态枚举
*/
enum SingletonEnum {
INSTANCE;
private Singleton singleton;
SingletonEnum() {
singleton = new Singleton();
}
private Singleton getInstance() {
return singleton;
}
}
public static Singleton getInstance() {
return SingletonEnum.INSTANCE.getInstance();
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> {
System.out.println(Singleton.getInstance().hashCode());
}).start();
}
}
}
5. 容器式单例模式
/**
* 容器式单例模式
*/
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
public static Object getBean(String className) {
Object singletonObject = singletonObjects.get(className);
if (singletonObject == null) {
synchronized (singletonObjects) {
singletonObject = singletonObjects.get(className);
if (singletonObject == null) {
try {
try {
singletonObject = Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
singletonObjects.put(className, singletonObject);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
return singletonObject;
}
}
spring框架中使用的就是容器式单例模式。
下面我们看看Spring源码 org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
4. 总结
单例模式有很多"变种",以上是总结的几种比较好的实现(也都是线程安全的),有小伙伴就疑问了,这么多该使用哪一种呢? 其实都可以使用,可以看自己爱好,可能工作上用得比较多的是第一种"饿汉式",个人比较推荐"静态内部类-单例模式",
以上大家很容易 发现 "容器式单例模式 " 也是 "懒汉模式-双重验证" 的变种,实际场景上我们完全可以根据需要适当定制,做到活学活用。
例如我在轻量级socket连接池实现中也使用了“容器式单例模式”。
public class ConnectionPool {
/**
* key is ip:port, value is ConnectionManager
*/
private final static ConcurrentHashMap<String, ConnectionManager> CP = new ConcurrentHashMap<String, ConnectionManager>();
public static Connection getConnection(InetSocketAddress socketAddress) throws MyException {
if (socketAddress == null) {
return null;
}
String key = getKey(socketAddress);
ConnectionManager connectionManager;
connectionManager = CP.get(key);
if (connectionManager == null) {
synchronized (ConnectionPool.class) {
connectionManager = CP.get(key);
if (connectionManager == null) {
connectionManager = new ConnectionManager(socketAddress);
CP.put(key, connectionManager);
}
}
}
return connectionManager.getConnection();
}
}
5、 支持
如果你觉得这篇内容对你挺有启发,我想请你帮二个小忙:
点个 [在看/转发],让更多的人也能看到这篇内容 (喜欢不点在看/转发,都是耍流氓 ????)
关注公众号 [浅谈架构], 公众号后台回复以下"编号" 或 "关键字",送你全套适合你的学习资料.
101: Java初级
102: Java高级进阶
103: Java面试