public class Apple {
private static Apple apple = new Apple();
private Apple() {
}
public static Apple getInstance() {
return apple;
}
}
以上为饿汉式单例模式简单实现,此种方式不会产生线程不安全情况,且只会创建一个对象。
饿汉式单例有以下特点
public class Banana {
private static Banana banana;
private Banana() {
}
public static Banana getInstance() {
if (banana == null) {
banana = new Banana();
}
return banana;
}
}
如果有多个线程调用getInstance
方法,都会进入if判断,从而造成重复实例化,不是真正的单例。
public class Banana {
private static Banana banana;
private Banana() {
}
public static Banana getInstance() {
synchronized (Banana.class) {
if (banana == null) {
banana = new Banana();
}
}
return banana;
}
}
上面的方式,虽然解决了线程安全问题,但是如果在初始化完成之后,每次调用获取还要再经过同步锁,多线程环境下会降低程序运行的效率。
public class Banana {
private static Banana banana;
private Banana() {
}
public static Banana getInstance() {
if (banana == null) {
synchronized (Banana.class) {
if (banana == null) {
banana = new Banana();
}
}
}
return banana;
}
}
第一层检查是只有实例还未初始化才进入同步锁,如果已经初始化完成就直接返回,这样可以提升效率。 第二层检查是为了防止前一个线程实例化之后释放锁,后面多个线程串行再去实例化,所以要加个判断。
public class Banana {
private static volatile Banana banana;
private Banana() {
}
public static Banana getInstance() {
if (banana == null) {
synchronized (Banana.class) {
if (banana == null) {
banana = new Banana();
}
}
}
return banana;
}
}
因为apple = new Apple()
创建对象有以下3个步骤:
如果没有禁止指令重排序,可能发生的顺序为1->3->2,就是在内存上开辟空间后就直接返回地址给引用,这个时候还没真正的初始化完对象。等释放锁后,别的线程进入判断,这时候引用已经不是null了,直接拿去使用,其实这个对象在这个时候可能还是个半成品,那就有空指针异常了。
懒汉式单例有以下特点
public class Cherry {
private static class CherryHolder {
private static Cherry instance = new Cherry();
}
private Cherry() {
}
/**
* 当getInstance方法第一次被调用的时候,执行CherryHolder.instance,导致CherryHolder类得到初始化;
* 而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Cherry的实例,
* 由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。
*/
public static Cherry getInstance() {
return CherryHolder.instance;
}
}
静态内部类单例有以下特点
public enum Durian {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}
枚举单例有以下特点
NoSuchMethodException
异常。CloneNotSupportedException
异常。除了枚举,其它几种单例模式都会被以上3种方式破坏。解决方案如下:
防止反射 定义一个全局变量,当第二次创建的时候抛出异常。
private static boolean isCreate = false;
private Apple() {
// 防止反射对单例的破坏
if(isCreate) {
throw new RuntimeException("已然被实例化一次,不能再实例化");
}
isCreate = true;
}
防止克隆破坏 重写clone(),直接返回单例对象。
@Override
protected Object clone() {
return apple;
}
防止序列化破坏 添加readResolve(),返回单例对象。
private Object readResolve() {
return apple;
}