单例模式在我们日常开发中其实也挺常见的,很多时候我们不喜欢别人对某个类创建太多的实例(亦或者需要频繁创建销毁某个对象,或者创建某个对象比较耗时消耗很多资源),那么我们就可以使用单例模式。单例模式的写法有很多,而有些写法其实纯在问题,那今天就在这篇文章中列举常见的单例写法并进行分析。
首先就是最常见的饿汉式写法:(饿汉是指太饥饿了,急需,所以一上来就先new一个出来)
public class SingleTonFactory {
private static final SingleTonFactory INSTANCE = new SingleTonFactory();
private SingleTonFactory() {
}
public static SingleTonFactory getInstance() {
return INSTANCE;
}
}
把构造函数设置为private,防止别人无限制的new出对象,然后我们需要了解一个知识点就是jvm对每个类只会加载一次,用这个static修饰的话,那么这个SingleTonFactory被加载到内存之后,就实例化了一个factory的单例,由此,该方法在jvm中是线程安全的。
还有一种是懒汉式:(就是比较懒,啥时候要啥时候再new)
public class SingleTonFactory {
private static SingleTonFactory INSTANCE = null;
private SingleTonFactory() {
}
public static SingleTonFactory getInstance() {
if (INSTANCE == null) {
INSTANCE = new SingleTonFactory();
}
return INSTANCE;
}
}
其实在单线程中正常使用时是没有啥问题的,但是到了多线程环境下,问题就出来了,如果线程一判断了INSTANCE == null,然后就往下走但是还没new,这时候另一个线程也走到了判断,为null,这时候线程二也往下走,那么线程一和线程二都各自new了一个实例,这就不符合单例模式了。然后就有人给这个单例过程加锁,经过不断的调优,最后变成下面这样的形式:
public class SingleTonFactory {
private static volatile SingleTonFactory INSTANCE; //volatile保证线程可见性
private SingleTonFactory() {
}
public static SingleTonFactory getInstance() {
if (INSTANCE == null) {
synchronized (SingleTonFactory.class) {
if (INSTANCE == null) {
INSTANCE = new SingleTonFactory();
}
}
}
return INSTANCE;
}
}
但是因为加锁了,所以执行效率方面肯定会存在一定的影响,本来时为了解决饿汉模式一加载就初始化的问题,结果修改后影响到性能,性价比不够。。。
所以后面有人用静态内部类的特性实现了一种单例:
public class SingleTonFactory {
private SingleTonFactory() {
}
private static class Holder {
private final static SingleTonFactory INSTANCE = new SingleTonFactory();
}
public static SingleTonFactory getInstance() {
return Holder.INSTANCE;
}
}
这里用到了jvm类加载的特性:加载一个类时,其内部类不会被加载,当且仅当内部类的静态成员被调用时才会加载内部类,而前面也提到了,jvm加载某个类时只会加载一次,这样一来,该INSTANCE只会被实例一次,保证了线程安全,还实现了懒加载。
发展到后面,出现了一种更牛逼的写法:
public enum SingleTonFactory {
INSTANCE;
}
单例模式大致就是以上几种写法了,不过在平时开发中,还是用饿汉式的单例模式比较多一些~