1.单例设计模式

1.单例设计模式

单例模式是指在系统中,使某些类只有一个实例存在,每次获取该实例对象时,总是获取到同一个对象Instance1==Instance2
现在操作系统的任务管理器就是以单例模式来实现的,重复打开也只是唤出同一个窗口。//我怎么记得以前XP还是Win7好像可以打开很多个
像各种连接池,也只需要存在一个实例即可。还有各类引擎、驱动等。

这个设计模式在实际中还是比较常见的一种。

单例模式有三个特点

  1. 单例实例只能通过单例类进行创建。
  2. 系统中至多只能存在一个该类的实例。
  3. 单例类需要对外提供一个方法来获取单例对象。

如果要使实例只能通过该类来进行创建,我们首先应该私有其构造函数,这样该类实例就无法在外部被直接创建出来。
然后我们再提供一个静态方法来供外部获取实例,并且每次获取的实例都是同一个。
单例模式的实现一般有以下几种。

1. 饿汉式单例

之所以叫饿汉式就是因为它很饿,一来就想要吃东西(在类被加载时就创建实例)。

/**
 * 饿汉式单例,在该类被加载时就创建了类的实例.
 * 唯一的缺点是不能懒加载,占用了资源.
 *
 * @author : hafuhafu
 * @date : 2019/9/11 15:17
 */
public class HungerSingleton {
    private static final HungerSingleton instance = new HungerSingleton();

    private HungerSingleton() {
    }

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

因为实例在类被加载的时候就开始创建,所以这种方式是线程安全的,并且实现简单,但是不能Lazy Load,相对来说会占用资源。

2. 懒汉式单例

2.1普通懒汉式

顾名思义,它很懒(不会在一开始就创建实例,和饿汉相反),只有当他第一次用到的时候(调用getInstance方法)的时候,才会创建实例。

/**
 * 懒汉式单例.优点是Lazy load,当需要调用的时候才会生成实例.但是该方式是线程不安全的.
 *
 * @author : hafuhafu
 * @date : 2019/9/11 15:05
 */
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
    }

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

}

懒汉式单例虽然在需要的时候才创建实例,相对而言节省了空间,但是在多线程情况下的有安全问题。

2.2线程安全的懒汉式

利用关键字 synchronized使获取实例的方法同步,但是同步加锁会影响性能。

/**
 * 线程安全的懒汉式,利用 `synchronized`关键字使方法同步.但是加锁会影响性能.
 *
 * @author : hafuhafu
 * @date : 2019/9/11 15:05
 */
public class SafeLazySingleton {
    private static SafeLazySingleton instance;

    private SafeLazySingleton() {
    }

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

}

2.3双检锁单例

/**
 * 双检锁单例,线程安全,性能较好.但并不推荐使用该方法
 * 虽然理论上完美,但是由于 `instance = new DoubleLockSingleton();` 并非原子操作.
 * 在JVM中,这句语句做了以下三件事:
 * 1.为 instance 分配内存空间
 * 2.调用DoubleLockSingleton的构造函数初始化成员变量
 * 3.将instance对象执行分配的内存空间(当这里执行完后,instance就不再为null)
 * 但是由于有些编译器中存在指令重排序的优化,导致 步骤2和3顺序不确定.
 * 这时我们将变量instance申明为 volatile,可以保证顺序.
 *
 * @author : hafuhafu
 * @date : 2019/9/12 10:19
 */
public class DoubleLockSingleton {
    private volatile static DoubleLockSingleton instance;

    private DoubleLockSingleton() {
    }

    public static DoubleLockSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleLockSingleton.class) {
                if (instance == null) {
                    instance = new DoubleLockSingleton();
                }
            }
        }
        return instance;
    }
}

但是我试着在多线程情况下测试了一下,无论有没有volatile关键字似乎都没啥差别,可能刚好我的编译器不会指令重排序吧。

3. 静态内部类单例

/**
 * 静态内部类实现单例,其实也是懒汉式,但是是利用JVM机制保证了单例以及线程安全.
 * 只有在 `StaticSingleton`的getInstance方法被调用的时候,SingletonHolder才会被加载,其中的INSTANCE才会被初始化.
 *
 * @author : hafuhafu
 * @date : 2019/9/12 10:39
 */
public class StaticSingleton {
    private static class SingletonHolder {
        private static final StaticSingleton INSTANCE = new StaticSingleton();
    }

    private StaticSingleton() {

    }

    public static final StaticSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

4.枚举实现单例

枚举是JDK1.5起提供的一个基本数据类型.本质上就是一个普通的类,但是其继承于java.lang.Enum

public enum TaskManagerEnum{
    INSTANCE;
}

编译后的字节码被反编译后得到如下代码;

public final class TaskManagerEnum extends Enum<TaskManagerEnum>{
      public static final TaskManagerEnum INSTANCE;
      public static TaskManagerEnum[] values();
      public static TaskManagerEnum valueOf(String s);
      static {};
}

每一个枚举类型以及其定义的枚举变量在JVM中都是唯一的。在枚举类型的序列化和反序列化上,Java做了特殊的规定。
在序列化时,Java仅仅将枚举对象的name属性输出到结果中,而反序列化时,则通过java.lang.EnumvalueOf()方法来根据名字查找枚举对象。
所以在序列化时,只将名称INSTANCE输出,反序列化时又通过这个名称查找,前后两个实例对象相同。
因此枚举天生保证序列化单例。

/**
 * 枚举由于机制原因,本身以及成员变量都是单例类型
 * 枚举是实现单例模式的最佳方式,功能完整,使用简洁,在序列化以及反射中都能够防止多次实例
 * 在<<Effective Java>>中被认为是最佳的实现方式
 *
 * @author : hafuhafu
 * @date : 2019/9/12 14:02
 */
public enum FileManager {
    INSTANCE;
    private String fileName;
    private long fileId;

    public static FileManager getInstance() {
        return INSTANCE;
    }
}
/**
 * 枚举在内部时.
 * 任务管理器是单例设计模式
 *
 * @author : hafuhafu
 * @date : 2019/9/12 13:58
 */
public class TaskManager {
    private String taskName;
    private long taskId;
    private List<Object> subList;

    enum TaskManagerEnum {
        INSTANCE;
        private TaskManager taskManager = new TaskManager();
    }

    private TaskManager() {
    }

    public static TaskManager getInstance() {
        return TaskManagerEnum.INSTANCE.taskManager;
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇