大家好,我是馆长!从今天开始馆长开始对java设计模式的创建型模式中的单例、原型、工厂方法、抽象工厂、建造者的单例模式进行讲解和说明。
单例模式(Singleton)
定义
某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
目的
节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。
特点
1.单例类只有一个实例对象;
2.该单例对象必须由单例类自行创建;
3.单例类对外提供一个访问该单例的全局访问点;
优点:
-
在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
-
避免对资源的多重占用(比如写文件操作)。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
结构
单例模式的主要角色如下。
·单例类:包含一个实例且能自行创建这个实例的类。
·访问类:使用单例的类。
注意点
1.单例模式是自关联关系设计。
2.字段 instance 为private,static, volatile修饰。
3.getInstance(全局访问点)为public ,static 修饰,其中涉及了 synchronized关键字的使用 。
4. 构造函数为private(私有的)。
volatile:
volatile关键字是为了保证线程安全的,当某个线程对共享变量只是读操作而没有写操作时,使用volatile就可以保证线程安全。volatile的作用是保证可见性和有序性,因为读操作本来就具有原子性,因此仅使用volatile就可以保证线程安全。
synchronized:
多个线程使用同一个对象进行加锁(基于对象头加锁),可以实现线程间的同步互斥。简单地说,它的原理就是基于对象头加锁,满足原子性、可见性、有序性。
懒汉式(线程安全)
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:易
描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class LazySingleton { //保证 instance 在所有线程中同步 private static volatile LazySingleton instance = null; private LazySingleton() { } //private 避免类在外部被实例化 //synchronized 修饰方法级别 public static synchronized LazySingleton getInstance() { //getInstance 方法前加同步 if (instance == null) { instance = new LazySingleton(); } return instance; } } |
懒汉式(线程不安全)
是否 Lazy 初始化:是
是否多线程安全:否
实现难度:易
描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
public class LazySingleton { private static LazySingleton instance; private LazySingleton (){} public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } } |
饿汉式(线程安全)
是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } } |
【推荐】双检锁/双重校验锁(DCL,即 double-checked locking)
JDK 版本:JDK1.5 起
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:较复杂
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } } |
登记式/静态内部类
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:一般
描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。调用getInstance()方法时才创建,达到了类似懒汉式的效果,同时又是线程安全的。
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } } |
枚举
JDK 版本:JDK1.5 起
是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
优点:线程安全,不用担心反射破坏单例模式,性能高,没有任何锁。
缺点:枚举类占用内存多。
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
public enum Singleton { INSTANCE; public void otherMethod() { //处理其他功能业务 } } |
23 种设计模式不是孤立存在的,很多模式之间存在一定的关联关系,在大的系统开发中常常同时使用多种设计模式,或者模式与模式之间的组合进行生成更加强大的程序功能。
好了,关于单例模式的说明,馆长就先讲到这里,最近馆长了解到单例模式实现不仅仅是上述的几种,随着软件行业的发展,各种变种也开始流行。由于时间和精力问题,馆长就先整理这些。谢谢各位看官!!
馆长准备了很多学习资料,其中包含java方面,jvm调优,spring / spring boot /spring cloud ,微服务,分布式,前端,js书籍资料,视频资料,以及各类常用软件工具,破解工具 等资源。请关注“IT技术馆”公众号,进行关注,馆长会每天更新资源和更新技术文章等。请大家多多关注。