1、懒汉模式。线程不安全,但可以延迟
【不推荐,不使用】
<span style="font-size:12px;">public class Singleton1 {
private static Singleton1 singleton1=null;
private Singleton1(){}
public static Singleton1 getIntance(){
return singleton1;
}
}</span>
注释: 这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。
2、懒汉模式。线程安全。但可以延迟。
使用情况:【不要求延迟时, 可以使用,不推荐】
<span style="font-size:12px;">class Singleton2{
private tatic Singleton2 singleton2=null;
private Singleton2(){}
public static synchronized Singleton2 getIntance(){
return singleton2;
}
}</span>
注释: 这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步
3、饿汉模式。 所有饿汉模式的单例都是线程安全的。
使用情况:【常用】
<span style="font-size:12px;">class Singleton3{
private static Singleton3 singleton3=new Singleton3();
private Singleton3(){}
public static Singleton3 getIntance(){
return singleton3;
}
}</span>
注释: 这种方式基于classloder机制避免了多线程的同步问题, 不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,
在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,
这时候初始化instance显然没有达到lazy loading的效果
4、饿汉模式。静态内部类
【推荐使用】
<span style="font-size:12px;">class Singleton4 {
private static class SingletonHolder {
private static final Singleton4 INSTANCE = new Singleton4();
}
private Singleton4 (){}
public static final Singleton4 getInstance() {
return SingletonHolder.INSTANCE;
}
}</span>
注释:这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):
第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,
instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。
想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。
5、双重校验锁 。
使用情况:【 推荐使用,常用,用吧,非常不错啦】
class Singleton5
{
private volatile static Singleton4 singleton;
private Singleton5 (){}
public static Singleton5 getSingleton() {
if (singleton == null) {
synchronized (Singleton5.class) {
if (singleton == null) {
singleton = new Singleton5();
}
}
}
return singleton5;
}
}
注释: 在双重检查锁中,代码会检查两次单例类 是否有已存在的实例,一次加锁一次不加锁,一次确保不会有多个实例被创建。声明
_instance
变量时使用了volatile关键字。没有volatile
修饰符,可能出现Java中的另一个线程看到个初始化了一半的_instance
的情况,但使用了volatile
变量后,就能保证先行发生关系(happens-before relationship)。对于volatile
变量_instance
,所有的写(write)都将先行发生于读(read)
6、枚举单例模式。
【好处不外乎三点:1.线程安全 2.不会因为序列化而产生新实例 3.防止反射攻击】
private Object readResolve(){
return INSTANCE;
}
注释:
代码就这么简单,你可以使用EasySingleton.INSTANCE调用它,比起你在单例中调用getInstance()方法容易多了。
下面是我总结的使用枚举实现单例模式的几个原因。(详细内容,其他网站上详细看,资源很多,我就不多说了)
枚举单例模式代码简洁 这是迄今为止最大的优点,如果你曾经在java5之前写过单例模式实现代码,那么你会知道即使是使用双检锁你有时候也会返回不止一个实例对象。虽然这种问 题通过改善java内存模型和使用volatile变量可以解决,但是这种方法对于很多初学者来说写起来还是很棘手。相比用 synchronization的双检索实现方式来说,枚举单例就简单多了。
用枚举实现的单例:这是我们通常写枚举单例的方式,它可能包含实例变量和实例方法,但是简单来说我什么都没用,需要注意的是如果你使用实例方法,你就需要确保方法的线程安全性,避免它会影响对象的状态。通常情况下枚举里面创建实例是线程安全的,但是其它的方法就需要编程者自己去考虑了。