单例模式

什么是单例模式

单例模式的类必须保证只有一个实例的存在, 许多时候我们的程序只需要拥有一个全局对象, 这样有利于我们协调系统整体的行为, 这时候就用到了单例模式

实现思路

一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

实现

饿汉法

饿汉法在第一次引用该类的时候就创建对象实例, 而不管实际是否需要用到, 这样写比较简单, 是线程安全的, 但是无法延迟加载, 如果没有要求懒加载的情况下, 直接使用饿汉法是比较合适的.

public class Singleton {
    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getSingleton() {
        return singleton;
    }
}

懒汉法

懒汉法是使用了懒加载模式的

public class Singleton {

    private static Singleton singleton = null;

    private Singleton() {
    }

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

这样写在单线程中是没有问题的, 但是在多线程下是可能不能正常工作的, 是线程不安全的, 下面修改为线程安全的, 最简单的就是将整个getInstance方法设置为同步:

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

这样虽然做到了线程安全, 但是是不高效的. 因为任何时候只有一个线程才能调用getInstance方法, 但是同步操作只是第一次调用的时候才需要被用到, 所以下面引出了双重检验锁:

public class Singleton {

    private static volatile Singleton singleton = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }

        return singleton;
    }
}

使用volatile限制了singleton可以保证其对所有的线程的可见性, 并禁止了JVM队形指令重拍优化.

静态内部类法

静态内部类法可以保证延时加载, 线程安全, 而且写法更加简单. 通过Singleton实例放到静态内部类中, 避免了静态实例在Singleton类加载的时候就创建对象, 并且由于静态内部类中只会被加载一次, 所以这种写法也是线程安全的.

public class Singleton {

    private static class Holder {
        private static Singleton singleton = new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getInstance() {
        return Holder.singleton;
    }
}

enum写法

如果需要实现序列化写法, Effective Java 上提供了一种非常简单的且提供序列化机制的写法:

public enum Singleton {
    INSTANCE;
}

参考资料:

  1. 你真的会写单例模式吗——Java实现 ImportNew
  2. Effective Java 第二版 第三条

ride
167 声望1 粉丝