本文共 2525 字,大约阅读时间需要 8 分钟。
单例模式Table of Contents
* 1. 饿汉模式 * 2. 懒汉模式以及演进 * 3. 静态内部类方式 * 4. ENUM方式为什么:
* 逻辑上,一个公司只能有一个老板 * 效率上,尽量少实例化对象避免空间占用 * 其他1 饿汉模式
最基本的思路,就是将类的构造器私有化,那么就不能在外部调用 new 创建实例了。
其次,通过调用静态方法获取实例。// 一般情况来说,这种方式就够用了!
public class Boss { private Boss() {} private static Boss instance = new Boss(); public static Boss getInstance() { return instance; }}
2 懒汉模式以及演进
饿汉模式的问题在于,即使没有用到 boss,它也会被实例化,有些浪费空间… 而懒汉模式就是让 boss 只在用到的时候才去加载。 其设计的思路及代码如下:public class Boss { // 1. 私有化构造器 private Boss {} // 2. 定义实例的变量 private static Boss instance; // 3. 通过静态方法创建或返回实例 public static Boss getInstance () { if (instance == null) { instance = new Boss(); // 虽然构造器是私有的,但是可以在内部调用 } return instance; }}
这种方法在单线程下没有任何问题,但是在多线程环境中,却可能会实例化出多个对象。
也就是说,它并不是线程安全的。为了解决这个问题,需要对 getInstance 加锁:
public class Boss { // 1. 私有化构造器 private Boss {} // 2. 定义实例的变量 private static Boss instance; // 3. 通过静态方法创建或返回实例 public synchronized static Boss getInstance () { // 通过锁,将对此方法的调用变成串行的。这就防止了错误 if (instance == null) { instance = new Boss(); // 虽然构造器是私有的,但是可以在内部调用 } return instance; }}
上述加锁的方式,可以保证正确实例化对象。但是,因为在方法上加了锁,使得获取单例对象的效率过低
这时候,需要兼顾线程安全和效率,就出现了双重检查锁的概念:
// 1. 将构造器私有化private Boss() {}// 2. 初始化一个静态变量private static volatile Boss instance = null;// 3. 构造一个静态方法,通过它初始化或返还对象public static Boss getInstance() {// 双重检查锁机制 if (instance == null) { synchronized (Boss.class) { if (instance == null) { instance = new Boss(); } }} return instance;}
其中:
* synchronized 块尽量缩小了锁定的范围,提高效率 * volatile 是为防止编译器指令重排而导致双重检查锁失效另外:
* 指令重排本是为了优化代码执行效率而存在的,虽然在单线程中效果拔群,但是在多线程中却能带来麻烦。volatile 可以要求编译器不要做指令重排。3 静态内部类方式
这是相对来说,非常优秀的一种实现。在很多地方,推荐使用这种方式。
public class Boss { // 1. 将构造器私有化 private Boss() { } // 2. 充分利用了静态内部类的特性,在里面初始化 Boss 实例 // - 只会被初始化一次 // - 只有当静态内部类内部的属性、方法等被调用的时候,静态内部类才会被加载 static class Singleton { private final static Boss INSTANCE = new Boss(); } // 3. 提供一个公共方法,获取实例化好之后的对象 public static Boss getInstance() { return Singleton.INSTANCE; }} 4 ENUM方式 ENUM 应该是最简单,也是最好的一种实现单例模式的方式。 它充分利用了 JVM 的特性,既保证了线程安全,又保证了延迟加载。 enum Boss { INSTANCE; public void sayHello () { System.out.println("hello"); } } public class Main { public static void main (String... args) { Boss theBoss = Boss.INSTANCE; // 获取实例 theBoss.sayHello(); // 调用方法 }}
转载地址:http://pthgn.baihongyu.com/