跳到主要内容

前言

单例模式是二十三种设计模式中最简单的一种设计模式了,他的应用情景有很多,比如我们在App中需要一个全局唯一的对象,这个时候我们就可以考虑使用单例模式了。

使用static提前加载

这种方式有一个弊端,就是我们无论是否使用这种方法,都会去提前加载这个单例对象,那么会出现一个问题,就是内存浪费问题,但是他却可以保证线程安全,并且他也没有加锁,效率也是十分的高。

public class Single{

private static Single Instance = new Single();

private Single(){}

public static Single getInstance(){
return Instance;
}
}

判断单例对象为null的时候采取加载

这样做在单线程情况下是没有任何问题的,并且我们这样做,也不会去浪费内存,但是弊端就是不能保证多线程环境下不出现问题。

public class Single{

private static Single Instance;

private Single(){}

public static Single getInstance(){
if(Instance == null){
Instance = new Single();
}
return Instance;
}
}

我们可以看到这样做,如果有两个线程同时判断Instance == null,那么他就会创建两个Single实例,这样就有失单例模式的含义了。

使用synchronized锁

基于上面的问题,我们可以考虑使用锁来保证线程安全的情况,我们都知道synchronized是一个比较重型的锁,所以虽然使用了这个锁来保证线程安全,但是,效率可想而知了

public class Single{

private static Single Instance;

private Single(){}

public static synchronized Single getInstance(){
if(Instance == null){
Instance = new Single();
}
return Instance;
}
}

基于synchronized优化

基于上面我们可以对他进行一个优化,也就是进行锁粒度上的优化

public class Single{

private static Single Instance;

private Single(){}

public static Single getInstance(){
if(Instance == null){
synchronized(Single.class){
if(Instance == null){
Instance = new Single();
}
}
}
return Instance;
}
}

我们可以看到这里先判断Instance是否为null,如果为null就给该类上锁,然后又判断是否为null,这样做是因为,如果两个线程同时判断Instance为null,会有一个线程拿到锁,另一个线程等待,第一个线程创建之后,然后释放锁,第二个线程此时拿到锁,我们如果不进行第二次判空的话,第二个线程仍然会创建一个新的Instance,这样就有失单例模式了。