单例方式
看到很多人写单例都不考虑线程安全,当多线程操作当时候,就可能出现多个实例,那么不就违背了单例的初衷了么?如果要维护到单例类中到成员,那就更尴尬了,我看到也实在是无奈,所以这里建议大家写单例是需要做到线程安全!!
首先说推荐单例模式写法,主流用法。
- DCL (Double CheckLock)
public class Singleton {
private Singleton() {
}
/**
* instance = new Singleton();因为不是一个原子操作,所以在编译到时候大致有三件事
* 1:给Singleton实例分配内存
* 2:调用好Sinleton() 构造函数,初始化成员字段
* 3:将instance对象指向分配到内存空间(执行了这一步后instance将为null了)
* 但是:
* 在JDK1.5之前有JMM(java Memory Model内存模型)中Cache、寄存器到驻内存回写顺序到规定,会导致执行顺序到不确定性
* 可能123 ,也可能132。
* 再1.5之后 JDK调整了JMM,加入了volatile关键字
*
* 这里加入volatile 关键字主要是为了保证每次都从驻内存获取
*/
private volatile static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
DCL写法优点就是在第一次执行到时候才会实例化,效率高。缺点就是第一次加载反应稍慢,性能牺牲点。再JDK1.6以后使用,这种方式是不错的选择。
- 静态内部类写法:
private Singleton() {
}
private static class SingletonClassHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonClassHolder.instance;
}
这是种单例方式,不光延迟了实例化,效率也高,也不需要双重检查锁定,并发上,性能就上去了。
- 枚举
public enum Singleton {
INSTANCE;
public void doS() {
Log.e("11", "11");
}
}
枚举最大的优点就是简单。枚举在java种与普通类式一样的,用这种方式来做单例也式很好的,最重要的是在任何情况下,它都式线程安全的,任何情况下它都只有一个实例。
其他单例在反序列化的时候会因为readResolve方法,而重新生成对象
//其他单例实现种,反序列化需要用这种方式来避免重新生成对象
private Object readResolve() throws ObjectStreamException{
return instance;
}
####另外 :
- 饿汉式:
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
看了前面,在看这个就明显了,在声明的时候就会实例化,也不管需不需要使用。所以一般不推荐使用,可根据应用场景选择。
- 懒汉式:
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
懒汉式式做到了再需要到时候才实例化了,但是每次获取的时候都需要同步,增加了开销,一般不推荐使用这种方式。
说说反射获取
介绍了5种单例方式,不线程安全的就不在这里说了。
除了枚举以外的方式,都可以使用反射来获取对象,采用如下方式即可:
Class<?> class1 = null;
try {
class1 = Class.forName("com.demo.Singleton");
Method m = class1.getMethod("getInstance");
Singleton single = (Singleton) m.invoke(null);
//获取到之后执行
single.doSomething();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
而枚举就不可以咯,会报错的。
所以枚举方式确实很不错。只是在android种不太推荐枚举,官方这样说的:
,会消耗更多的内存,影响性能问题。对手机内存不大的,是个灾难。
根据实际情况来选择对应的单例模式就可以了。