单例
一、简介
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。
实现方式
1、构造函数私有化:作用是在其他代码地方不能new新的该对象的实例。
2、该类的内部提供一个静态方法,当调用该方法时返回该类的引用(如果为空就创建类的实例)。
二、单例的八种实现方式
1、饿汉式
见名知意,一开始就创建类的实例,不管是否能用到。
优点:写法比较简单,就是在类装载的时候就完成实例化。在多线程使用中也能保证一个实例。
缺点:在类装载过程中就完成实例化,如果程序用不到的话,就会造成内存的浪费。
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
2、饿汉式(静态代码块)
此种方式是方法1的扩展,当类加载时候执行静态代码块中的代码,完成类的实例化。
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
3、懒汉式
见名知意:用到该类时才会去创建该类的实例。
缺点:多线程先会造成多个实例的创建(原因时当执行到singleton == null时其他线程也进入会先创建对象的实例)
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
4、懒汉式(synchronized关键字方法)
多线程中可以实现一个实例,但是效率低。
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
5、懒汉式(synchronized关键字代码块)
多线程不能实现一个线程的实例化,原因和方法3一样。
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
6、双重检查(即方法4和5的结合)
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
7、静态内部类
当singleton类被加载的时候,此时内部类并不会被加载。当调用getInstance方法时内部类才会被加载,完成实例化。
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
8、枚举
不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
枚举类中没有构造方法。所以不可以通过反射拿到该类。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
三、总结
在多线程情况下可以保证单例的方法:饿汉式及其扩展、synchronize同步方法、synchronized的双重检查、静态内部类和枚举的方式。
推荐使用:双重检查、静态内部类、枚举的方式。实际使用中饿汉式使用较多。
面试题:
为什么双重检查为什么要加volatile?
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}