创建型模式--单例模式
单例模式
单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:1)单例类中只能有一个实例
2)单例类必须自己创建自己的唯一实例
3)单例类必须给所有其他对象提供这一实例
介绍
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,没有则创建。
关键代码:构造函数是私有的。
应用实例:一个班级只有一个班主任。
优点:1)在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。2)避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
应用场景:1)要求生产唯一序列号。2)WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。3)创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
实现
我们将创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。SingleObject 类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo,我们的演示类使用 SingleObject 类来获取 SingleObject 对象。
1、创建一个Singleton 类。
public class SingleObject { private static SingleObject instance = new SingleObject(); private SingleObject(){} public static SingleObject getInstance(){ return instance; } public void showMessage(){ System.out.println("hello world!"); } }
2、从 Singleton 类获取唯一的对象。
public class SingletonPatternDemo { public static void main(String[] args) { //不合法,构造函数不可见 //SingleObject so = new SingleObject(); SingleObject so = SingleObject.getInstance(); so.showMessage(); } }
输出:hello world!
单例模式的几种实现方式
1、懒汉式不加锁,线程不安全。可能会有多个进程进入到 if 语句内,创建多个实例。
/* * 懒加载,线程不安全。 * */ public class SingletonLazyUnlocked { private static SingletonLazyUnlocked instance; private SingletonLazyUnlocked(){} public static SingletonLazyUnlocked getInstance(){ if(instance != null){ instance = new SingletonLazyUnlocked(); } return instance; } }
2、懒汉式加锁,线程安全。加synchronized影响效率。
/* * 懒加载,线程安全。 * */ public class SingletonLazyLocked { private static SingletonLazyLocked instance; private SingletonLazyLocked(){} public static synchronized SingletonLazyLocked getInstance(){ if(instance != null){ instance = new SingletonLazyLocked(); } return instance; } }
3、饿汉式,线程安全。类加载时创建实例,容易产生垃圾对象。
/* * 非懒加载,线程安全。 * */ public class SingletonHungry { private static SingletonHungry instance = new SingletonHungry(); private SingletonHungry(){} public static SingletonHungry getInstance(){ return instance; } }
4、双重校验锁(Double-Checked Locking)
/* * 懒加载,线程安全 * */ public class SingletonDCL { private volatile static SingletonDCL instance; private SingletonDCL(){} public static SingletonDCL getInstance(){ if(instance == null){ synchronized (SingletonDCL.class){ if(instance == null){ instance = new SingletonDCL(); } } } return instance; } }
第一次校验:如果创建了实例再去执行getInstance()方法不会去竞争锁,直接返回实例。
第二次校验:防止二次创建实例。如果没有第二个判空语句,在实例还没有创建的情况下,A 和 B 两个线程都要获得单例的对象。A ,B 都进入了第一个判空语句内,然后争夺锁,A获得了锁,实例化了一个对象,然后释放了锁,B获得了锁,由于没有第二个判空语句B 又创建了一次对象。
volatile 关键字的作用:防止指令重排优化。保证变量在多线程运行时的可见性,无法保证原子性。
5、静态内部类
对静态域使用延迟初始化利用类加载机制来保证初始化 instance 时只有一个线程。
/* * 静态内部类懒加载 * 懒加载,线程安全 * */ public class SingletonRegister { private static class SingletonHolder{ private static final SingletonRegister INSTANCE = new SingletonRegister(); } private SingletonRegister(){} public static final SingletonRegister getInstance(){ return SingletonHolder.INSTANCE; } }
6、枚举
实现单例的最佳方法。更简洁,自动支持序列化机制防止多次实例化。
public enum SingletonEnum { INSTANCE; public void method(){ } }
参考:https://www.runoob.com/design-pattern/singleton-pattern.html