单例模式

单例模式

单例模式(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();
        }
    }
}

posted @ 2020-03-28 13:12  lzhya  阅读(155)  评论(0)    收藏  举报