单例模式
单例模式是应用最广的模式。在某些状态下,需要某个对象需要仅保持一个实例的存在。例如在ImageLoader中,需要保持缓存,线程池等的唯一,这时候就需要用到单例。
实现单例一般有四种。
- 饿汉式
- 懒汉式
- 双重锁模式
- 内部类模式
饿汉式
class Singleton{
private static Singleton instance = new Singleton;
private Singleton(){
}
private static Singleton getInstance(){
return instance;
}
}
饿汉式是在类加载的时候就创建唯一的类对象。即使未调用getInstance()
,仍会创建对象。资源比较浪费。
懒汉式
懒汉式是在需要时在创建唯一的类对象。
class Singleton{
private static Singleton instance = null;
private Singleton(){
}
private static synchronized Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
懒汉式相比较饿汉式而言,虽然解决了资源浪费的问题,但仍存在一个问题,就是当调用getInstance()
方法时,当instance
不为null时,仍会因为线程同步而导致资源被浪费,这也是最大的问题。
优点:
- 只有在资源需要被使用时才会被实例化,这在一定程度上也会解决资源浪费的问题。
缺点:
- 第一次加载时需要及时加载实例化,反应稍慢。最大的问题是调用
getInstance()
时,会造成不必要的同步开销。
双重锁模式
双重锁模式( Double CheckLock) - > DCL模式
class Singleton{
private static Singleton instance = null;
private Singleton(){
}
private static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
该方法的特点就是在判断instance==null 的时候判断了两次,很大程度上的解决了同步开销浪费的问题。
第一层判断是为了避免不必要的同步问题。
第二层判断是为了在null的时候创建单例实例。
但该方法的的主要问题如下:
对于instance = new Singleton();这条语句中,在实际的JVM执行中会分成三个步骤进行执行
- 给singleton的实例分配内存。
- 调用Singleton的构造方法,初始化成员字段。
- 将instance指向所分配的内存空间。
在实际的运行中,因为其执行是乱序的,所以无法保证2和3的执行的先后顺序。则会出现如下情况,出现如下运行顺序,即运行1,3,2.则假设运行到3时,突然有线程B调用getInstance()
。因为此时程序已经调用了3方法,instance
不为null,则线程B直接返回了instance对象,此时程序会出现问题。
该方法能够在需要的时候实例化对象,并且能够在绝大多数场景下保证单例对象的唯一性,除非你的代码能够在并发场景比较复杂或者低于JDK 6版本使用,否则这种方式一般能够满足需求。 —引用自《Android源码设计模式解析与实战》
内部类模式
class Singleton{
private Singleton(){
}
public static class SingletonHolder{
private static final Singleton instance = new Singleton();
}
private static Singleton getInstance(){
return SingletonHolder.instance;
}
}
对于该方法,在不调用getInstance()
方法时,SingletonHolder
不会被加载,当我们调用时,才会使sintance
实例化。那么,即当我们第一次创建调用getInstance()
时,对象才会被加载,。该方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。
此种方式最为推荐