1 单例模式介绍
所谓单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
2 懒汉式与饿汉式
单例模式的实现,大致分为懒汉式实现与饿汉式实现。
饿汉式实现是指在类装载的时候就完成实例化,没有达到懒加载的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
懒汉式实现是指只有当使用到这个类时,才进行实例化。
3 饿汉式单例模式实现
饿汉式单例模式实现较为简单,一般采用静态常量的方式来实现:
package com.qzq.singleton.type1;
public class SingleTonTest01 {
public static void main(String[] args) {
//测试
SingleTon instance1 = SingleTon.getInstance();
SingleTon instance2 = SingleTon.getInstance();
System.out.println(instance1==instance2);
}
}
//饿汉式写法
class SingleTon{
//1. 构造器私有化,外部无法new
private SingleTon(){
}
//2. 本类内部创建对象实例
private final static SingleTon instance = new SingleTon();
//3. 提供一个静态方法,来返回实例对象
public static SingleTon getInstance(){
return instance;
}
}
4 懒汉式单例模式实现
懒汉式单例模式重点在于线程安全问题。
4.1 双重检查实现
package com.qzq.singleton.type5;
public class SingleTonTest05 {
public static void main(String[] args) {
SingleTon instance1 = SingleTon.getInstance();
SingleTon instance2 = SingleTon.getInstance();
System.out.println(instance1==instance2);
}
}
class SingleTon{
//volatile保证可见性
private static volatile SingleTon instance;
private SingleTon(){}
//提供一个静态公用方法,当使用到该方法时,才去创建instance
//加入同步处理的代码,解决线程不安全问题
public static SingleTon getInstance(){
if(instance == null){
synchronized(SingleTon.class){
if(instance==null){
instance = new SingleTon();
}
}
}
return instance;
}
}
synchronized Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
在代码中,synchronized() 括号中的是SingleTon这个类,表示锁定SingleTon类,此时其他线程执行到这里会陷入等待。
4.2 枚举实现
package com.qzq.singleton.type7;
public class SingleTonTest7 {
public static void main(String[] args) throws InterruptedException {
System.out.println("hhh");
Thread.sleep(1000);
SingletonObject instance1 = SingletonObject.getInstance();
SingletonObject instance2 = SingletonObject.getInstance();
System.out.println(instance1==instance2);
}
}
class SingletonObject {
private SingletonObject(){
}
/**
* 枚举类型是线程安全的,并且只会装载一次
*/
private enum Singleton{
INSTANCE;
private final SingletonObject instance;
Singleton(){
System.out.println("loading");
instance = new SingletonObject();
}
private SingletonObject getInstance(){
return instance;
}
}
public static SingletonObject getInstance(){
return Singleton.INSTANCE.getInstance();
}
}
使用枚举方法防止反序列化重新创建新的对象。
除枚举方式外, 其他方法都会通过反射的方式破坏单例,反射是通过调用构造方法生成新的对象。使用枚举方法可以防止使用者通过反射来破坏单例模式。
4.3 其他单例实现防止单例被破坏
非枚举实现想要阻止单例破坏,可以在构造方法中进行判断,若已有实例, 则阻止生成新的实例,解决办法如下:
private SingletonObject1(){
if (instance !=null){
throw new RuntimeException("实例已经存在,请通过 getInstance()方法获取");
}
}
如果单例类实现了序列化接口Serializable, 就可以通过反序列化破坏单例,所以我们可以不实现序列化接口,如果非得实现序列化接口,可以重写反序列化方法readResolve(), 反序列化时直接返回相关单例对象。
public Object readResolve() throws ObjectStreamException {
return instance;
}
5 参考资料
- https://zhuanlan.zhihu.com/p/80127173