核心作用
- 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常见应用场景
- 任务管理器
- 回收站
- 网站的计数器
- 日志的管理
- 数据库连接池
- 操作系统中的文件系统
- Application (Servlet中)
- Spring中,每个Bean默认是单例的
- Servlet中每个Servlet是单例的
- spring MVC框架/status框架中,控制器对象也是单例的
常见的五种单例模式实现方式
懒汉式(单例对象延迟加载)
public class SingletonDemo(){
private static SingletonDemo instance;
private SingletonDemo(){}
public static synchronized SingletonDemo getInstance(){
if(instance==null){
instance = new SingletonDemo();
}
return instance;
}
}
饿汉式(单例对象立即加载)
public class SingletonDemo(){
private static SingletonDemo instance = new SingletonDemo();
private SingletonDemo(){
}
public static SingletonDemo getInstance(){
return instance;
}
}
双重检测锁(将同步内容下放到if内部)
- 提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步创建了以后就没必要了
- 问题 1 由于编译器优化原因和JVM底层内部模型原因偶尔会出问题,不建议使用。
public class SingletonDemo3 {
private static SingletonDemo3 instance = null;
private SingletonDemo3() {}
public static SingletonDemo3 getInstance() {
if (instance == null) {
SingletonDemo3 sc;
synchronized (SingletonDemo3.class) {
sc = instance;
if (sc == null) {
synchronized (SingletonDemo3.class) {
if(sc == null) {
sc = new SingletonDemo3();
}
}
instance = sc;
}
}
}
return instance;
}
}
静态内部类(也是一种懒加载)
- 外部类没有static属性,不会出现像懒加载那样立即加载
- 只有真正调用getInstance()才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证了内存中只有这样的一个实例存在,而且只被赋值一次,从而保证了线程安全性
- 兼备了并发高效调用和延迟加载的优势
public class SingletonDemo4 {
private static class SingletonClassInstance {
private static final SingletonDemo4 instance = new SingletonDemo4();
}
private SingletonDemo4(){}
public static SingletonDemo4 getInstance(){
return SingletonClassInstance.instance;
}
}
枚举单例
- 实现简单
- 枚举本身就是单例模式,线程安全的,由JVM从根本上提供保障,避免通过反射和反序列化的漏洞
- 无延迟加载
public enum SingletonDemo5 {
INSTANCE;
public void singletonOperation(){
}
}
public static void main(String[] args){
SingletonDemo5 sd1 = SingletonDemo5.INSTANCE;
SingletonDemo5 sd2 = SingletonDemo5.INSTANCE;
System.out.println(s1==s2);
}
单例模式的破解和反破解
public void reflect() throws Exception {
SingletonDemo sd1 = SingletonDemo.getInstance();
SingletonDemo sd2 = SingletonDemo.getInstance();
System.out.println(sd1==sd2);
Class<SingletonDemo> clzz = (Class<SingletonDemo>) Class.forName("ren.kanxue.designmode.SingletonDemo");
Constructor<SingletonDemo> constructor = clzz.getDeclaredConstructor(null);
constructor.setAccessible(true);
SingletonDemo sd3 = constructor.newInstance();
SingletonDemo sd4 = constructor.newInstance();
System.out.println(sd3==sd4);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/a.txt"));
oos.writeObject(sd1);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
SingletonDemo s5 = (SingletonDemo) ois.readObject();
System.out.println(s5);
}
public class SingletonDemo implements Serializable {
private static SingletonDemo instance;
private SingletonDemo(){
if(instance!=null){
throw new RuntimeException();
}
}
public static synchronized SingletonDemo getInstance(){
if(instance==null){
instance = new SingletonDemo();
}
return instance;
}
private Object readResolve() throws ObjectStreamException {
return instance;
}
}