1:定义
作为对象的创建模式,单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类被称为单例类
2:特点
单例类只有一个实例
必须由自己创建自己的唯一实例(构造器私有)
单例类提供提供唯一一个访问该实例的访问点
3:优点
单例模式只生成了一个实例,减少了系统性能的开销,当一个对象的产生需要比较多的资源时如读取配置文件或产生其他依赖对象时,则可以通过产生一个单例对象然后永久驻留内存的反方式来解决
单例模式可以在系统设置全局的访问点,优化环共享资源访问
4:常见的五种实现方式
- 饿汉式
- 懒汉式
- 双重检测锁式
- 静态内部类式
- 枚举式
5:具体代码实现
1:饿汉式
package com.xiaoqiang.gof23.createModel.singleton;
import java.io.ObjectStreamException;
/**
* @author xiaoqiang
*饿汉式单例模式
*/
public class SingletonDemo1 {
/**饿汉式单例模式
* 在加载的时候直接初始化时,立即加载这个并且创建这个对象,不能延迟
* */
private static SingletonDemo1 singletonDemo1 = new SingletonDemo1();
private SingletonDemo1() {
if (singletonDemo1 != null) {
// 防止反射破解单例
throw new RuntimeException();
}
}
/* 由于加载类时是天然的线程安全的 不用synchronized 同步锁,效率高*/
public static /*synchronized*/ SingletonDemo1 getsingletonDemo1() {
return singletonDemo1;
}
/*防止反序列化破解单例*/
/*定义readResolve()则直接返回此方法的对象,不用单独在创建新的对象*/
private Object readResolve() throws ObjectStreamException {
return singletonDemo1;
}
}
饿汉式,顾名思义,饿了,在这个类被加载时,静态变量singletonDemo1会被初始化,此时类的私有构造子会被调用。这时候,单例类的唯一实例就被创建出来了。
饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问
题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。
特点:线程安全,调用效率高,但不能延迟加载。
2:懒汉式
package com.xiaoqiang.gof23.createModel.singleton;
/**
* @author xiaoqiang
* 懒汉式
*/
public class SingletonDemo2 {
// 懒汉式,不初始化对象,延迟加载,需要的时候创建,要用到线程锁 synchronized,效率低了
private static SingletonDemo2 singletonDemo2;
private SingletonDemo2() {
}
public static synchronized SingletonDemo2 getSingletonDemo2() {
if (singletonDemo2 == null) {
singletonDemo2 = new SingletonDemo2();
}
return singletonDemo2;
}
}
懒汉式,实现了延迟加载(懒加载)不初始化对象,只有在用到时才创建实例对象,但是每次调用getSingletonDemo2()方法要同步,并发,效率低
3:双重检测锁式
package com.xiaoqiang.gof23.createModel.singleton;
/**
* @author xiaoqiang
*/
public class SingletonDemo3 {
/**双重检查锁单例模式实现
* 将同步放到if的内部,提高了执行的效率
* 问题:由于编译器优化和jvm内部模型的原因,偶尔会出现问题,不建议使用
*
* */
private static SingletonDemo3 singletonDemo3;
private SingletonDemo3() {
}
private static SingletonDemo3 getSingletonDemo3() {
if (singletonDemo3 == null) {
SingletonDemo3 s;
synchronized (SingletonDemo3.class) {
s = singletonDemo3;
if (s == null) {
synchronized (SingletonDemo3.class) {
if (s == null) {
s = new SingletonDemo3();
}
}
singletonDemo3=s;
}
}
}
return singletonDemo3;
}
}
双重检测锁:这个模式将同步内容下放到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步
创建了以后就没必要了,但是由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题。不建议使用
4:静态内部类式
package com.xiaoqiang.gof23.createModel.singleton;
/**
* @author xiaoqiang
* @date $(DATE)-$(TIME)
* 静态内部类实现单例模式
* 线程安全,调用效率高,延迟加载
*
* 在加载SingletonDemo4时,不会加载SingletonClassInstance,当使用到才会加载,所以是延迟加载
*/
public class SingletonDemo4 {
private static class SingletonClassInstance {
// 保证线程安全
private static final SingletonDemo4 instance = new SingletonDemo4();
}
public static SingletonDemo4 getSingletonDemo4() {
return SingletonClassInstance.instance;
}
private SingletonDemo4() {
}
}
外部类没有static属性,则不会像饿汉式那样立即加载对象。
–只有真正调用getSingletonDemo4(),才会加载静态内部类。加载类时是线程 安全的。 instance是static final
类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性.
– 兼备了并发高效调用和延迟加载的优势!
5:枚举式
package com.xiaoqiang.gof23.createModel.singleton;
/**
* @author xiaoqiang
* 枚举实现单例
* 枚举本身就是单例,简单,避免反射和反序列化漏洞来创建对象
* 没有懒(延迟)加载
*/
public enum SingletonDemo5 {
/**
* 定义一个枚举元素,它就代表一个单例
* */
INSTANCE;
/**
* 单例有自己的操作
*/
public void singletonOperation() {
/**功能处理*/
}
}
枚举式:实现简单,本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!,但是不能实现延迟加载
补充,尽管是单例模式,但是还是可以通过反射和反序列化的方式类破解单例模式(枚举式除外),创建不只一个对象实例
防破解的方法在SingletonDemo1中的代码中有了,其他跟着实现就行
具体方法
反射可以破解上面几种(不包含枚举式)实现方式! (可以在构造方法中手动
抛出异常控制)
– 反序列化可以破解上面几种((不包含枚举式))实现方式!
• 可以通过定义readResolve()防止获得不同对象。
– 反序列化时,如果对象所在类定义了readResolve(),就会返回这个方法的返回值(实际是一种回调)