单例模式
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例
1. 单例模式的应用场景
JavaEE标准中的ServletContext,ServletContextConfig等;
Spring框架应用中的ApplicationContext;
数据库的连接池。
2.1饿汉式单例模式
方法1:静态方法获得私有变量
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
方法2:利用静态代码块与类同时加载的特性生成单例模式
public class Singleton {
public static final Singleton singleton ;
static{
singleton=new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
是否多线程安全:是
描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,不管用不用都占用内存,造成内存浪费
源码:Spring中IOC容器ApplicationContext本身就是典型的单例模式.*
2.2 懒汉式单例模式
方法1 多线程不安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
方法2 多线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:第一次调用才初始化,避免内存浪费(被外部类调用的时候才会被加载)。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
方法3 双检锁/双重校验锁
public class Singleton {
// volatile解决指令重排序
//1.分配内存给这个对象
//2.初始化对象
//3.设置 lazy 指向刚分配的内存地址
private volatile staticSingleton instance;
private Singleton() {
}
public static Singleton getInstance() {
//检查是否要阻塞,第一个instance == null是为了创建后不再走synchronized代码,提高效率。
if (instance == null) {
synchronized (Singleton.class) {
//检查是否要重新创建实例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
JDK 版本:JDK1.5 起
是否多线程安全:是
优点:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
不足:当第一个线程调用 getInstance()方法时,第二个线程也可以调用。当第一个线程执行到synchronized时会上锁,第二个线程就会出现阻塞状态。但是,阻塞并不是基于整个类的阻塞,而是在getInstance()方法内部的阻塞。
方法4 静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
兼顾了饿汉式单例模式的内存浪费问题和 synchronized 的性能问题。内部类一定是要在方法调用之前初始化,巧妙地避免了线程安全问题。
3 反射破坏单例模式
public class ReflectTest {
public static void main(String[] args) {
try {
Class<?> clazz = Singleton.class;
//通过反射获取私有的构造方法
Constructor c = clazz.getDeclaredConstructor(null);
//强制访问
c.setAccessible(true);
//初始化实例对象
Object instance1 = c.newInstance();
Object instance2 = c.newInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance1 == instance2);
}catch (Exception e){
e.printStackTrace();
}
}
}

浙公网安备 33010602011771号