一文看懂Java Singleton单例模式

172 阅读3分钟

定义: 在整个运行过程中,一个类只有一个实例对象
为什么要用?
有些对象创建消耗资源,如果频繁创建和销毁那将会造成性能浪费,所以我们要用单例模式

常用的一种方法

public class Singleton {
	private volatile static Singleton singleton;
	private Singleton () {

	}
	public static  Singleton getSingleton() {
		if (null  == singleton) {
			synchronized (Singleton.class) {
				if (null == singleton) {
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}
class main {
	public static void main(String[] args) {
		System.out.println(Singleton.getSingleton());

	}
}

主要从1、是否线程安全 2 是否懒加载 3 能否反射破坏(人为)

===========================
下面是其他各类方法
一:只适用单线程环境(不推荐)

/**
 * @author kegekeqi
 * @version 1.0
 * @date 2021-12-7 7:50
 */
public class Javasingleton {
	private Javasingleton() {

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



二、加同步锁前后两次判断实例是否存在,也称为双重检测锁
好处:只有当instance为null时,需要获取同步锁,创建一次实例。当实例被创建,则无需试图加锁。
缺点:用双重if判断,复杂,容易出错。

/**
 * @author kegekeqi
 * @version 1.0
 * @date 2021-12-7 7:50
 */
public class Javasingleton {
	private Javasingleton() {

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


可能到这里了,大家会提起经常说到的懒汉式、饿汉式和枚举,不要急一一列举,哪种最好用,好好思考一下。
三、饿汉模式

public class HungryManMode {
    private static HungryManMode hungryManMode = new HungryManMode();
    private HungryManMode(){};
    public static HungryManMode getHungryManMode(){
        return hungryManMode;
    }
}


饿汉模式在类加载的时候就对实例进行创建,避免了多线程重复创建实例的问题,但是类加载就创建一定程度造成了内存的浪费。

四、懒汉模式

public class LazyManMode {
    private static LazyManMode lazyManMode = null;
    private LazyManMode(){};
    public static synchronized LazyManMode getLazyManMode(){
        if(lazyManMode == null){
            lazyManMode = new LazyManMode();
        }
        return lazyManMode;
    }
}


懒汉模式中在单例需要的时候才进行创建,为了解决存在的并发问题,使用synchronized关键字 进行约束,也就造成了一定的性能浪费

五、枚举

// Singledto.class
public class Singledto implements Serializable {}

// EnumSingleton.class
public enum EnumSingleton {
    ;
    private Singledto singledto;

    private EnumSingleton(){
        singledto = new Singledto();
    }

    public Singledto getSingledto(){
        return singledto;
    }
}


枚举中的实例被保证只会被实例化一次,所以需要的实例也会保证实例化一次,枚举强烈推荐

六、静态内部类(建议使用)
按需创建实例
由于私有的属性,他人无法使用SingleHolder,不调用SingletonHolder.getInstance()就不会创建实例。

public class JavaSingleton {
	private JavaSingleton() {

	}
	private static class SingletonHolder {
		private final static JavaSingleton instance = new JavaSingleton();
	}
	public static JavaSingleton getInstance() {
		return SingletonHolder.instance;
	}
}

除此之外,还有利用C#的静态构造函数,确保只创建一个实例。还有利用私有嵌套类型的特性做到真正需要的时候才会创建实例,提高空间使用效率,减少资源的浪费。
补充:比如懒汉式可以人为用反射去破坏,防止这种事情发生可以用枚举类。

枚举类也有它的缺点
枚举类不可继承。并且使用枚举时占用的内存比静态变量的2倍还多。
各种方式的优缺点

目前要有完美的方案使得1、是否线程安全 2 是否懒加载 3 能否反射破坏(人为)这些条件都满足,几乎没有,就好比牺牲了时间换空间,牺牲了空间换时间,通常3发生比较少,我们更关注1、2条件是否满足。

本文使用 文章同步助手 同步