目录
一.单例模式的定义
定义:确保其一个类只有一个实例,且自行实例化并向整个系统提供这个实例。
单例模式的实现:
通过private构造函式确保在一个应用中只产生一个实例。且自行实例化。外界通过get()函数来获取这个实例。
单例模式使用场景:
- 要求生成唯一序列号的环境;
- 整个项目中需要一个共享访问点或共享数据;
- 创建一个对象需要消耗的资源过多;
- 需要定义大量的静态常量和静态方法(如工具类)的环境;
单例模式的优点:
- 减少内存开销;
- 减少系统的性能开销(但要注意JVM的垃圾回收机制);
- 避免对资源的多重占用;
- 可以在系统中设置全局访问点。
单例模式的缺点:
- 单例模式一般没有接口,扩展困难;
- 单例模式对测试不利,如果单例模式没有完成,便不能进行测试;
- 单例模式与单一职责原则有所冲突。
注意事项:
克隆问题:在Java中对象默认不可复制,但是如果实现了Cloneable接口的clone()方法,则单例模式就失去了其作用。因为对象的复制不调用类的构造函数,即使构造函数私有,对象依旧可以被复制。
单例模式类图:
单例模式Java模板:
public class Singleton {
private static final Singleton singleton = new Singleton();
//限制产生多个对象
private Singleton() {
//通过该方法创造对象
}
public static Singleton getStingleton() {
return singleton;
//通过该方法获取实例对象
}
public static void doSomething() {
//类中的其他方法尽量定义为static
}
}
public class Singleton {
private static final Singleton singleton = null;
//限制产生多个对象
private Singleton() {
//通过该方法创造对象
}
public static Singleton getStingleton() {
if(singleton==null) {
singleton=new Singleton();
}
return singleton;
//通过该方法获取实例对象
}
public static void doSomething() {
//类中的其他方法尽量定义为static
}
}
二.单例模式与多线程
1.立即加载:“饿汉模式”
立即加载便是实用类的时候已经将对象创建完毕,通常实现的办法为直接new实例化。
饿汉式是线程安全的,因为虚拟机保证了只会装载一次,在装载类的时候是不会发生并发的。
2.延迟加载:“懒汉模式”
延迟加载就是在调用get()方法时实例才被创建,常见的实现方法为在get()方法中进行new()实例化。
存在的问题:
在多线程环境下,延迟加载只简单这样设计就会是错误的,这样根本不能保证单例状态。会创建出多个实例。
解决办法:
- 声明synchronized锁即synchronized方法:给get()方法加锁。
- 使用同步代码块:get()方法加锁,有可能会导致运行效率低下,这时可以使用同步代码块来对判断分支语句加锁;
- 使用DCL双检查锁机制:同步代码块可以针对某些重要的代码进行单独的同步,而其他的代码不需要同步这样效率可以大幅提高。具体实现为,用.class锁来对new语句进行加锁。但这样仍然有可能会产生多个实例,解决的办法为在锁内再判断一次实例是否已创建。DCL是大多数多线程结合单例模式使用的解决办法。
3.静态内置类实现单例模式
使用DCL模式可以解决线程问题,也可以使用其他方法来实现多线程下的单例模式。