【创建型模式五】单例模式

一、单例模式

单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。

通常我们可以通过调用类的静态变量来使一个对象被访问,但它不能防止你实例化多个该对象。一个最好的办法就是让类自身负责保存它的唯一实例,这样就可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。

二、实现单例模式的三个必要条件

  • 构造函数私有化;
  • 用于保存实例化后的对象的静态变量私有化;
  • 获取实例的方法共有化

三、单例模式的角色

Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的 getInstance() 工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个 Singleton 类型的静态对象,作为外部共享的唯一实例。

四、单例模式实现方式

单例模式除了能保证唯一的实例外,还可以控制客户怎样访问它以及何时访问它。简单地说就是对唯一实例的受控访问。
通常有五种方式来实现单例模式,懒汉式饿汉式双重校验加锁式静态内部类式枚举类式

4.1 懒汉式

需要使用时才实例化对象,节约内存。但多线程情况下可能会有多个线程同时判断singleton == null而实例化多个LazySingleton对象。

public class LazySingleton {
   
    private static LazySingleton singleton = null;
    private LazySingleton() {
   }
    public static LazySingleton getInstance() {
   
        if (singleton == null) {
    
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

4.2 饿汉式

在类加载期间直接实例化一个单例,就算不使用它,也会占用内存。但线程安全。

public class HungrySingleton {
   
    private static HungrySingleton singleton = new HungrySingleton();
    private HungrySingleton() {
   }
    public static HungrySingleton getInstance() {
   
        return singleton;
    }
}

4.3 双重校验加锁式

懒汉式的线程安全形式,但获取锁和释放锁需耗费一定的CPU处理时间。

public class DoubleCheckSingleton {
   
    private volatile static DoubleCheckSingleton singleton = null;//要加volatile,保证线程间的可见性
    private DoubleCheckSingleton() {
   }
    public static DoubleCheckSingleton getInstance() {
   
        if (singleton == null) {
   //不让线程每次访问getInstance方法都加锁,只有在没有实例化时才加锁
            synchronized (DoubleCheckSingleton.class) {
    //锁的是类对象,不是实例对象singleton,因为singleton不一定被创建出来了
                if (singleton == null) {
    //如果有多个线程同时通过了第一层校验,在获得锁后如果不再校验一次,有可能实例已经被创建过了
                    singleton = new DoubleCheckSingleton();
                }
            }
        }
        return singleton;
    }
}

4.4 静态内部类式

静态内部类只有在被调用时才会被加载,加载时会初始化这个内部类的静态变量,由虚拟机保证只会被初始化一次,这样就同时实现了延迟加载和线程安全。

public class StaticInnerClassSingleton {
   
    private StaticInnerClassSingleton() {
   }
    private static class Singleton {
   
       public static StaticInnerClassSingleton singleton = new StaticInnerClassSingleton();
    }
    public static StaticInnerClassSingleton getInstance() {
   
        return Singleton.singleton;
    }
}

4.5 枚举类式

枚举类实质上是功能齐全的类,因此可有自己的属性和方法,枚举是通过公有的静态final域为每个枚举常量导出实例的类。简洁高效安全,由虚拟机保障不会被实例化多次。

public enum EnumSingleton {
    enumSingleton;
}

五、单例模式的安全性

单例模式的目标是,任何时候该类都只有唯一的一个对象。但是上面我们写的大部分单例模式都存在漏洞,被攻击时会产生多个对象,破坏了单例模式。

5.1 序列化攻击

通过Java的序列化机制来攻击饿汉式单例模式:

public class HungrySingleton {
   
    private static final HungrySingleton instance = new HungrySingleton();
    private HungrySingleton() {
   
    }
    public static HungrySingleton getInstance() {
   
        return instance;
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
   
        HungrySingleton singleton = HungrySingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
        oos.writeObject(singleton); // 序列化

        ObjectInputStream ois = new ObjectInputStream(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值