设计模式之单例模式
单例即单一的实例,确切的将是在系统中,只存在一个实例
1.饿汉模式
1.1 静态成员变量
// 静态成员变量
public class Singleton {
// 私有化构造方法
private Singleton() {
}
// 静态成员变量
private static final Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance.equals(instance1));
for (int i = 0; i < 10; i++) {
// 打印的结果都一样
new Thread(() -> System.out.println(Singleton.getInstance())).start();
}
}
}
1.2 静态代码块
// 静态代码块
public class Singleton {
// 私有化构造方法
private Singleton() {
}
// 静态成员变量
private static final Singleton instance;
static{
instance = new Singleton();
}
// 每个静态代码块只会被执行一次
public static Singleton getInstance() {
return instance;
}
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance.equals(instance1));
for (int i = 0; i < 10; i++) {
// 打印的结果都一样
new Thread(() -> System.out.println(Singleton.getInstance())).start();
}
}
}
静态成员变量
和静态代码块
的方式都可以实现单例模式,饿汉式单例模式在类加载时就创建好了实例对象,不必等到调用获取实例方法时才创建对象。饿汉式单例模式是线程安全的,如果该实例对象没有地方使用,会造成内存的浪费。
2.懒汉式
2.1 非线程安全
public class Singleton {
// 私有化构造方法
private Singleton() {
}
// 静态成员变量
private static Singleton instance;
public static Singleton getInstance() throws InterruptedException {
if (instance == null) {
TimeUnit.SECONDS.sleep(1);
instance = new Singleton();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// 打印的结果不都一样,说明出现了线程安全问题
new Thread(() -> {
try {
System.out.println(Singleton.getInstance());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
打印结果为:
com.stone.designpattern.singleton.Singleton@7b111699
com.stone.designpattern.singleton.Singleton@33da527e
com.stone.designpattern.singleton.Singleton@230a2e2c
com.stone.designpattern.singleton.Singleton@7cd50f6a
com.stone.designpattern.singleton.Singleton@61811c3c
com.stone.designpattern.singleton.Singleton@230a2e2c
com.stone.designpattern.singleton.Singleton@2e00fa9
com.stone.designpattern.singleton.Singleton@2ce81cfe
com.stone.designpattern.singleton.Singleton@18d4dffc
com.stone.designpattern.singleton.Singleton@1feeb6a3
2.2 synchronized改进(线程安全)
public class Singleton {
// 私有化构造方法
private Singleton() {
}
// 静态成员变量
private static Singleton instance;
public static synchronized Singleton getInstance() throws InterruptedException {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
使用synchronized修饰获取实例对象的方法,满足线程安全,但是效率降低。
2.3 双重校验锁(线程安全)
public class Singleton {
// 私有化构造方法
private Singleton() {
}
// 静态成员变量
private static volatile Singleton instance;
public static Singleton getInstance() throws InterruptedException {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重校验锁的实现,是同步代码块,效率相较同步方法要高,同时我们使用
volatile
来修饰变量,是为了指令重排。
2.4 静态内部类(线程安全)
public class Singleton {
// 私有化构造方法
private Singleton() {
}
// 静态内部类
private static final class InstanceHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return InstanceHolder.instance;
}
}
静态内部类不会随外部类的加载而加载,能够保证对象只被实例化一次,保证线程安全。
3.序列化破环单例模式
public class Singleton implements Serializable {
private static final long serialVersionUID = -6614362446349716995L;
// 私有化构造方法
private Singleton() {
}
// 静态内部类
private static final class InstanceHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return InstanceHolder.instance;
}
public static void main(String[] args) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./a.txt"));
Singleton instance = Singleton.getInstance();
System.out.println(instance);
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./a.txt"));
Object o1 = ois.readObject();
System.out.println(o1);
oos.close();
ois.close();
}
}
打印结果:
com.stone.designpattern.singleton.Singleton@1a407d53
com.stone.designpattern.singleton.Singleton@3551a94
3.1 解决方案
定义readResolve()方法
private Object readResolve() {
return InstanceHolder.instance;
}
在ObjectInputStream类的readObject()方法中,调用了如下方法,这里我们提取关键部分代码,重要的就是desc.hasReadResolveMethod(),如果说存在readResolve()方法,就会去反射调用该方法,然后将得到的对象返回。
private Object readOrdinaryObject(boolean unshared) throws IOException{
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()){
}
}
4.反射破坏单例模式
public class Singleton {
// 私有化构造方法
private Singleton() {
}
// 静态内部类
private static final class InstanceHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return InstanceHolder.instance;
}
public static void main(String[] args) throws Exception {
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
Singleton singleton = constructor.newInstance();
Singleton singleton1 = constructor.newInstance();
System.out.println(singleton);
System.out.println(singleton1);
}
}
com.stone.designpattern.singleton.Singleton@566776ad
com.stone.designpattern.singleton.Singleton@6108b2d7
打印结果,我们发现得到多个实例对象,单例模式被反射破坏
4.1 解决方案
public class Singleton {
private static boolean flag = false;
// 私有化构造方法
private Singleton() {
synchronized (Singleton.class) {
System.out.println(flag);
if (flag) {
throw new RuntimeException();
}
flag = true;
}
}
// 静态内部类
private static final class InstanceHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return InstanceHolder.instance;
}
public static void main(String[] args) throws Exception {
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
Singleton singleton = null;
try {
singleton = constructor.newInstance();
System.out.println(singleton);
} catch (Exception exception) {
exception.printStackTrace();
}
}).start();
}
}
}
以上就是单例模式的所有内容,我将持续更新设计模式,欢迎大家一起交流😁