单例模式是保证一个类仅有一个实例,并提供一个全局访问点。属于创建型,也属于GOF23种设计模式。
适用范围
想确保任何情况下都绝对只有一个实例。
优缺点
优点:在内存里只有一个实例,减少了内存开销;可以避免对资源的多重占用;设置全局访问点,严格控制访问。
缺点:没有接口,扩展困难。
示例
懒汉式解决方案
我们可以使用 synchronized
锁来实现线程安全。
方式一:但是在 static
方法中使用 synchronized
锁是针对整个类的,是一个类锁(全局锁),锁的范围比较大,导致加锁解锁资源开销也比较大。下面示例中 getInstance1
和 getInstance2
等同。
package com.zaiae.design.pattern.creational.singleton;
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {
}
public static LazySingleton getInstance1() {
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
}
return lazySingleton;
}
public synchronized static LazySingleton getInstance2() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
方式二:使用 volatile
关键字来“禁止指令重排序”,并采用双重检查方式,减少 synchronized
加锁解锁的次数,从而解决效率和线程安全问题。
package com.zaiae.design.pattern.creational.singleton;
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton;
private LazyDoubleCheckSingleton() {
}
public static LazyDoubleCheckSingleton getInstance() {
if (lazyDoubleCheckSingleton == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (lazyDoubleCheckSingleton == null) {
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazyDoubleCheckSingleton;
}
}
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
注意:在上面示例中,创建对象并不是直接完成的。事实上会先分配对象的内存空间,然后初始化对象并让 lazyDoubleCheckSingleton
指向内存空间,最后才被线程访问。其中初始化对象和指向对象的内存空间并不是一定按顺序执行的,这样在多线程环境下就有可能发生对象还未初始化,就被其他线程访问了,从而导致异常发生。
所以我们可以使用 volatile
关键字来保证创建对象过程中不会出现重排序,从而避免这个问题。
方式三:基于类初始化的延迟加载,我们可以使用静态内部类的方式实现。在多线程环境中,当一个线程访问这个静态内部类,会添加一个 Class
对象的初始化锁( InnerClass
对象的初始化锁),让这个静态内部类初始化时,对象实例化过程中的重排序对其他线程不可见。
package com.zaiae.design.pattern.creational.singleton;
public class StaticInnerClassSingleton {
private static class InnerClass {
private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return InnerClass.staticInnerClassSingleton;
}
private StaticInnerClassSingleton() {
}
}
饿汉式解决方案
方式一:在类加载的时候就直接创建单例对象。
package com.zaiae.design.pattern.creational.singleton;
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
方式二:通过静态代码块创建单例对象。
package com.zaiae.design.pattern.creational.singleton;
public class HungrySingleton {
private static final HungrySingleton INSTANCE;
static {
INSTANCE = new HungrySingleton();
}
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
序列化和反序列化
我们自己创建的类,实现 Serializable
接口,就能够实现序列化和反序列化。但是会导致单例对象被破坏,造成对象不是同一个,我们可以通过实现 readResolve()
方法解决这个问题。
package com.zaiae.design.pattern.creational.singleton;
import java.io.Serializable;
public class HungrySingleton implements Serializable {
private static final HungrySingleton INSTANCE;
static {
INSTANCE = new HungrySingleton();
}
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return INSTANCE;
}
private Object readResolve() {
return INSTANCE;
}
}
Test.java
测试类:
package com.zaiae.design.pattern.creational.singleton;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
HungrySingleton instance = HungrySingleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
oos.writeObject(instance);
File file = new File("singleton_file");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
HungrySingleton newInstance = (HungrySingleton) ois.readObject();
System.out.println(instance);
System.out.println(newInstance);
System.out.println(instance == newInstance);
}
}
运行结果:
com.zaiae.design.pattern.creational.singleton.HungrySingleton@266474c2
com.zaiae.design.pattern.creational.singleton.HungrySingleton@266474c2
true
已加入收藏夹,时不时的来看看有没有更新博文!
天气越来越冷了,躲在家里刷刷博客也挺好!