设计模式-02-单例模式

设计模式专题-02-单例模式

个人github地址:HibisciDai

设计模式系列项目源码:HibisciDai/DesignPattern-LearningNotes-HibisciDai

processon在线UML类图:processon

[TOC]

设计模式-02-单例模式

单例模式(Singleton Pattern)

意图

一个类仅有一个实例,并提供一个访问它的全局访问点。

注意

  1. 该类不能被外界任意实例化(构造函数私有化)。
  2. 该类向外界提供一个可以获得该类实例的方法。
  3. 该类只能被实例化一次。

主要解决

一个全局使用的类频繁的创建与销毁。

关键代码

构造函数是私有的。

应用实例

一个党只有一个主席。

优点

  1. 在内存中只有一个实例,减少内存开销。
  2. 避免资源的多重占用。

缺点

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外边如何实例化。

其它

getInstance() 方法中需要使用同步锁 synchronized(Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SingleObject {
private static SingleObject instance = new SingleObject();

private SingleObject() {
}

public static SingleObject getInstance() {
return instance;
}

public void show() {
System.out.println("hello SingleObject");
}
}

//main函数入口简写,下同
Main {
//错误初始化 SingleObject object = new SingleObject();
SingleObject object = SingleObject.getInstance();
object.show();
}

单例模式的几种实现方式

懒汉式,线程不安全

  • 是否Lazy初始化

  • 是否多线程安全

  • 描述

最基本的实现方式,不支持多线程和线程安全,严格来说并不是多线程。

  • 代码实例
1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton instance;

private Singleton() {};

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

懒汉式,线程安全

  • 是否Lazy初始化

  • 是否多线程安全

  • 描述

可在多线程中很好的工作,但是效率低,必须加锁 synchronized 才能保证单例,但加锁会影响效率。getInstance() 的性能对应用程序不是很关键

  • 代码实例
1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton instance;

private Singleton() {};

public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

饿汉式

  • 是否Lazy初始化

  • 是否多线程安全

  • 描述

常用方式,但容易产生垃圾对象。基于classloade机制避免了多线程的同步问题,instance在类装载的时候就实例化。

  • 优点

没有加锁,执行效率会提高。

  • 缺点

类加载时就初始化,浪费内存。

  • 代码实例
1
2
3
4
5
6
7
8
9
public class Singleton {
private static Singleton instance = new Singleton();

private Singleton() {};

public static Singleton getInstance() {
return instance;
}
}

双检锁/双重校验锁(DCL,即double-checked locking)

jdk1.5之后支持

  • 是否Lazy初始化

  • 是否多线程安全

  • 描述

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。 getInstance() 的性能对应用程序很关键。

  • 代码实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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初始化

  • 是否多线程安全

  • 描述

可以达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
该方式利用 classloader 机制保证初始化 instance 时只有一个线程
与第三种方式不同的是:第三种方式只要 Singleton 类被装载了,那么 instance 就会被实例化;
该方式 Singleton 类被装载了,instance不一定被初始化。
因为 SingletonHolder 类没有被主动使用,只有显式调用 getInstance 方法时,才会显示装载 SingletonHolder 类,从而实例化 instacne。

  • 代码实例
1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton6 {
private static class SingletonHolder {
private static final Singleton6 INSTANCE = new Singleton6();
}

public Singleton6() {
}

private static final Singleton6 getInstance() {
return SingletonHolder.INSTANCE;
}
}

枚举

jdk1.5之后

  • 是否Lazy初始化

  • 是否线程安全

  • 描述

这种实现方式还没有被广泛采用,是最佳方法,避免多线程,支持序列化,绝对防止多次实例化。
不能通过 reflection attack 来调私有构造方法。

  • 代码实例
    1
    2
    3
    4
    5
    publc enum Singleton {
    INSTANCE;

    public void whateverMethod() {}
    }

关于一些经验

不建议1 2方式,建议3

明确实现 lazy loading 时候,使用5

涉及序列化,用6

其它特殊需求,考虑4

文章作者: HibisciDai
文章链接: http://hibiscidai.com/2018/03/14/设计模式-02-单例模式/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HibisciDai
好用、实惠、稳定的梯子,点击这里