单例模式的定义
单例模式是指确保一个类在任何情况下都只有一个实例,并且提供一个访问该单例的全局访问点。
如何创建一个单例模式
构造函数需要是 private 访问权限的,这样才能避免外部通过 new 创建实例;
考虑对象创建时的线程安全问题;
考虑是否支持延迟加载;
考虑 getInstance() 的性能(是否加锁)
单例模式适用的场景
J2EE 标准中的 ServletContext 和 ServletContextConfig;
Spring 框架应用中的 ApplicationContext、数据库中的连接池等也都是单例模式。
饿汉式(饥渴型)单例模式
在类加载的时候就创建对象,不会出现线程安全问题(因为还没有开始加载的时候就创建好了对象,线程都没有机会去争夺)。
package SingletonFactoryModel;
public class SingletonHungry {
//成员变量 在类加载的时候就把对象创建好了
static SingletonHungry s = new SingletonHungry();
// 1.构造方法私有化
private SingletonHungry(){}
// 返回就是私有类已经创建好的对象
private static SingletonHungry getInstance(){
return s;
}
public static void main(String[] args) {
SingletonHungry instance = SingletonHungry.getInstance();
SingletonHungry instance1 = SingletonHungry.getInstance();
System.out.println(instance1 == instance);
}
}
懒汉式(懒狗)单例模式
懒汉式:在需要的时候才创建,类加载时不创建对象, 直接创建对象可能会有线程安全问题,导致重复创建不同对象,通过施加 双重校验锁来实现对同一对象的获取
package SingletonFactoryModel;
public class SingletonLazy {
// 通过 volatile 关键字保证 数据的原子性
/*
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
禁止进行指令重排序。
*/
private volatile static SingletonLazy singletonLazy;
private SingletonLazy(){}
public static SingletonLazy getInstance() {
// 检查是否需要创建
if (singletonLazy == null) {
// synchronized 保证同一时刻只有一个对象去调用
synchronized (SingletonLazy.class){
// 因为前面施加了同步块 可能会导致 两个线程都校验为空重复创建对象,需要再次判断为空
if(singletonLazy == null){
singletonLazy = new SingletonLazy();
}
}
}
return singletonLazy;
}
public static void main(String[] args) {
SingletonLazy instance = SingletonLazy.getInstance();
SingletonLazy instance1 = SingletonLazy.getInstance();
System.out.println(instance == instance1);
}
}