单例模式

简介

  • 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式的写法

饿汉模式

  • 线程安全,通过JVM的类加载机制保证其只会有一个实例。
  • 写法示例
package designpattren.hungrysingleton;

/**
 * 饿汉单例模式
 */
public class HungrySingletonTest {
    public static void main(String[] args) {
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance1 = HungrySingleton.getInstance();
        System.out.println(instance == instance1);
    }
}

/**
 * 饿汉单例模式,这种方式是线程安全的,通过JVM的类加载机制可以保证每个类只会被加载一次
 * 类加载过程:
 * 1、加载二进制数据到内存中,生成对应的CLass数据结构
 * 2、连接:a、验证   b、准备,给类的静态成员赋默认值,c、解析
 * 3、初始化:给类的静态变量赋初值
 *
 */
class   HungrySingleton{
    private static HungrySingleton instance =new HungrySingleton();
    private  HungrySingleton(){

    }
    public static HungrySingleton getInstance(){
        return  instance;
    }

}

懒汉模式

  • 线程不安全,用volatile关键字,防止JIT进行指令重排,导致的空指针问题,使得其可以在多线程环境下使用。
  • 写法示例
package designpattren.lazysingleton;

/**
 * 懒汉单例模式
 */
public class LazySingletonTest {
    public static void main(String[] args) {
//        LazySingleTon instance = LazySingleTon.getInstance();
//        LazySingleTon instance1 = LazySingleTon.getInstance();
//        System.out.println(instance == instance1);
        new Thread(()->{
            LazySingleTon instance =LazySingleTon.getInstance();
            System.out.println(instance);
        }).start();
        new Thread(()->{
            LazySingleTon instance =LazySingleTon.getInstance();
            System.out.println(instance);
        }).start();
    }
}
class LazySingleTon{
    //使用volatile关键字,防止JIT进行指令重排,导致的空指针问题
    private volatile static LazySingleTon  instance;
    private LazySingleTon(){

    }
    //全局访问点
    public  static LazySingleTon getInstance(){
        if (instance ==null){
            synchronized (LazySingleTon.class){
                if(instance==null){
                    instance = new LazySingleTon();
                    /**
                     * JIT,CPU可能会对2.3进行重排
                     * 字节码层
                     * 1、分配空间
                     * 2、初始化
                     * 3、引用赋值
                     */
                }
            }

        }
        return instance;
    }
}

静态内部类单例模式

  • 线程安全,通过静态内部类实现单例,同样依赖JVM的类加载机制保证它的线程安全。
  • 写法示例
package designpattren.innerclasssingleton;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class InnerClassSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
//        对Instance进行序列化
        InnerClassSingleton instance = InnerClassSingleton.getInstance();
        ObjectOutputStream oos  = new ObjectOutputStream(new FileOutputStream("testSerializable"));
        oos.writeObject(instance);
        oos.close();
        //反序列化
          ObjectInputStream  ois =  new ObjectInputStream(new FileInputStream("testSerializable"));
          Object object =(InnerClassSingleton)ois.readObject();

        System.out.println(instance == object);
    }
}

/**
 * 基于静态内部类实现单例模式
 * 依赖JVM的类加载机制保证线程安全
 */
class InnerClassSingleton  implements Serializable {
    static final long  serialVersionUID =  42L;
    private  static  class  InnerClassHolder{
        private static InnerClassSingleton   instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
        if(InnerClassHolder.instance!=null){
            throw  new  RuntimeException("单例不允许多个实例");
        }
    }
    public static  InnerClassSingleton getInstance(){
        return  InnerClassHolder.instance;
    }
    Object readResolve() throws ObjectStreamException{
        return  InnerClassHolder.instance;
    }
}

枚举单例模式

  • 使用枚举实现的单例模式也是线程安全的,写法也比较简单,缺点是可读性比较低。
  • 这种方式下,也可以防止序列化攻击。
  • 写法示例
package designpattren.enumsingleton;



import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

public enum EnumSingleton{
    INSTANCE;
    public void print(){
        System.out.println(this.hashCode());
    }
}
class EnumTest{
    public static void main(String[] args) throws Exception {
        //Enum不支持反射,线程安全
//        Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, Integer.class);
//        declaredConstructor.setAccessible(true);
//        declaredConstructor.newInstance("INSTANCE",0);
        EnumSingleton instance = EnumSingleton.INSTANCE;
//        ObjectOutputStream oos  = new ObjectOutputStream(new FileOutputStream("test EnumSingleton"));
//        oos.writeObject(instance);
//        oos.close();
        //反序列化
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream("test EnumSingleton"));
        Object object =( EnumSingleton)ois.readObject();
        System.out.println(object ==  instance);
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值