设计模式--单例模式
一. 定义
显而易见单例就是创建一个实例
二. 单例的创建方式
- 懒汉式
- 饿汉式
- 静态内部类
- 枚举
- 懒汉式实现:
package design.singlePattern;
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
public static void main(String[] args) {
LazySingleton lazySingleton1 = LazySingleton.getInstance();
LazySingleton lazySingleton2 = LazySingleton.getInstance();
//结果为true
System.out.println(lazySingleton1 == lazySingleton2);
}
}
上述代码执行结果为true,确实为单例,但实际多线程并发的时候并不是单例,可能就创建了多例:
package design.singlePattern;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.execute(() -> {
System.out.println(LazySingleton.getInstance());
});
//打印的结果为:
//design.singlePattern.LazySingleton@3c85a950
//design.singlePattern.LazySingleton@2918e06d
//design.singlePattern.LazySingleton@1ecb2403
//design.singlePattern.LazySingleton@30633415
//design.singlePattern.LazySingleton@3c85a950
//所以在多线程场景下,可能就不是单例了
}
}
}
所以需要加同步锁,避免多线程访问创建多个实例 [synchronized]
synchronized 可以加在静态方法【锁的是类对象】,成员方法【锁的当前创建的对象/this】,方法内部同步块【锁的是当前同步块的区域】,所以建议锁加载同步块上,减小锁的范围,提高对象调用效率
同时对LazySingleton对象进行volatile,主要防止即时编译或cpu指令重排,在这里可能很多人不理解为什么加volatile,创建实例对象在java代码层面不就是new LazySingleton()一句代码吗?怎么还涉及到指令重排了?
实际可以查看字节码新建一个实例对象到底做了那些步骤:
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: new #2 // class design/singlePattern/LazySingleton
8: dup
9: invokespecial #3 // Method "<init>":()V
12: putfield #4 // Field lazySingleton:Ldesign/singlePattern/LazySingleton;
15: return
LineNumberTable:
字节码层面实际 给对象开辟空间对应上面字节码的【对应上述步骤5】 -> init【9】-> 赋值 【12】,多步就会涉及到指令重排,所以就要加volatile关键字防止指令重排
package design.singlePattern;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LazySingleton {
private volatile static LazySingleton lazySingleton;
private LazySingleton() {
}
public static LazySingleton getInstance(){
if(lazySingleton == null) {
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.execute(() -> {
System.out.println(LazySingleton.getInstance());
});
//design.singlePattern.LazySingleton@4c1ea472
//design.singlePattern.LazySingleton@4c1ea472
//design.singlePattern.LazySingleton@4c1ea472
//design.singlePattern.LazySingleton@4c1ea472
//design.singlePattern.LazySingleton@4c1ea472
}
}
}
从上面的结果可以看出,多线程也可以实现单例
- 饿汉式实现
package design.singlePattern;
public class HungrySingleton {
private static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
public static void main(String[] args) {
//true
System.out.println(HungrySingleton.getInstance() == HungrySingleton.getInstance());
}
}
由于饿汉式是类加载后就完成了初始化,jvm可以保证线程安全
类加载的流程: 类加载器加载(双亲委派机制) -> 验证(文件格式、数据等校验) -> 准备(开辟空间赋默认值,常量直接赋初始值) -> 解析(符号引用转为地址引用) -> 初始化(静态变量、静态变量赋初始值)
- 静态内部类实现
package design.singlePattern;
public class InnerClassSingleton {
private InnerClassSingleton(){
}
private static class InnerClass{
private static InnerClassSingleton innerClassSingleton = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance(){
return InnerClass.innerClassSingleton;
}
public static void main(String[] args) {
// true
System.out.println(InnerClassSingleton.getInstance() == InnerClassSingleton.getInstance());
}
}
- 枚举实现
package design.singlePattern;
public enum EnumSingleton {
singleton;
public static void main(String[] args) {
//true
System.out.println(EnumSingleton.singleton == EnumSingleton.singleton);
}
}
三. 懒汉式、饿汉式、以及静态内部类单例模式存在的问题
- 懒汉式:反射创建对象非单例(无法避免)
package design.singlePattern;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazySingleton {
private volatile static LazySingleton lazySingleton;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<LazySingleton> lazySingletonConstructor = LazySingleton.class.getDeclaredConstructor();
lazySingletonConstructor.setAccessible(true);
LazySingleton lazySingleton = lazySingletonConstructor.newInstance();
//false
System.out.println(lazySingleton == LazySingleton.getInstance());
}
}
- 饿汉式:反射创建对象非单例
package design.singlePattern;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class HungrySingleton {
private static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
//由于构造方法为private所以不能直接通过构造获取对象
//HungrySingleton hungrySingleton = HungrySingleton.class.getConstructor().newInstance();
//Class类的getDeclaredConstructor()方法,可获取到类的私有构造器(包括带有其他修饰符的构造器)
//但在使用private的构造器时,必须设置setAccessible()为true,才可以获取并操作该Constructor对象
Constructor<HungrySingleton> hungrySingletonConstructor = HungrySingleton.class.getDeclaredConstructor();
hungrySingletonConstructor.setAccessible(true);
HungrySingleton hungrySingleton = hungrySingletonConstructor.newInstance();
// false 通过反射获取的对象非单例
System.out.println(hungrySingleton == HungrySingleton.getInstance());
}
}
可以通过构造方法判断是否为非单例扔调用异常避免:
package design.singlePattern;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class HungrySingleton {
private static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){
if (hungrySingleton != null) {
throw new IllegalCallerException("私有方法不允许调用");
}
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
//由于构造方法为private所以不能直接通过构造获取对象
//HungrySingleton hungrySingleton = HungrySingleton.class.getConstructor().newInstance();
//Class类的getDeclaredConstructor()方法,可获取到类的私有构造器(包括带有其他修饰符的构造器)
//但在使用private的构造器时,必须设置setAccessible()为true,才可以获取并操作该Constructor对象
Constructor<HungrySingleton> hungrySingletonConstructor = HungrySingleton.class.getDeclaredConstructor();
hungrySingletonConstructor.setAccessible(true);
HungrySingleton hungrySingleton = hungrySingletonConstructor.newInstance();
System.out.println(hungrySingleton == HungrySingleton.getInstance());
}
}
- 静态内部类:反射创建对象非单例
package design.singlePattern;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class InnerClassSingleton {
private InnerClassSingleton(){
}
private static class InnerClass{
private static InnerClassSingleton innerClassSingleton = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance(){
return InnerClass.innerClassSingleton;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
//false
System.out.println(InnerClassSingleton.getInstance() == innerClassSingleton);
}
}
可以通过构造方法判断是否为非单例扔调用异常避免:
package design.singlePattern;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class InnerClassSingleton {
private InnerClassSingleton(){
if (InnerClass.innerClassSingleton != null) {
throw new IllegalCallerException("私有方法不允许调用");
}
}
private static class InnerClass{
private static InnerClassSingleton innerClassSingleton = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance(){
return InnerClass.innerClassSingleton;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
//false
System.out.println(InnerClassSingleton.getInstance() == innerClassSingleton);
}
}
单例序列化再反序列化后非同一个实例对象:主要由于反序列化并不会调用构造方法,而是根据流读取生成对象
例如:饿汉模式
package design.singlePattern;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class HungrySingleton implements Serializable {
private static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){
if (hungrySingleton != null) {
throw new IllegalCallerException("私有方法不允许调用");
}
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("HungrySingletonSerializable"));
oos.writeObject(HungrySingleton.getInstance());
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("HungrySingletonSerializable"));
HungrySingleton hungrySingleton = (HungrySingleton) ois.readObject();
//false
System.out.println(HungrySingleton.getInstance() == hungrySingleton);
}
}
避免序列化后反序列化非单例,设定序列化版本号,且替换反序列化读取流数据生成对象的方式:
package design.singlePattern;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
public class HungrySingleton implements Serializable {
**private static final long serialVersionUID = 42L;**
private static HungrySingleton hungrySingleton = new HungrySingleton();
private String str;
private HungrySingleton(){
if (hungrySingleton != null) {
throw new IllegalCallerException("私有方法不允许调用");
}
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
Object readResolve() throws ObjectStreamException {
return hungrySingleton;**
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("HungrySingletonSerializable"));
oos.writeObject(HungrySingleton.getInstance());
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("HungrySingletonSerializable"));
HungrySingleton hungrySingleton = (HungrySingleton) ois.readObject();
//true
System.out.println(HungrySingleton.getInstance() == hungrySingleton);
}
}
总结:单例虽然是设计模式中最简单的模式,但是涉及的内容繁多,希望可以帮助到大家

浙公网安备 33010602011771号