Java设计模式--单例模式【Singleton Pattern】

来源:互联网 发布:大专女生知乎 编辑:程序博客网 时间:2024/06/03 09:19

      在宇宙中,太阳是唯一的,独一份的,不可复制的,人们每天抬头看到的太阳都是同一个,不会随着斗转星移就多产生一个太阳,所以太阳就是绝对的单例。下面就以“太阳”为例介绍单例模式。

     所谓单例模式就是保证一个类有且只有一个实例。要创建一个独一无二的对象实例,就必须保证程序其他地方不能创建出该单例对象的新实例。

    设计一个单例类时,需要确定何时初始化该类的单例对象。

    1、第一种做法是创建这个类的实例,并将它作为该类的静态成员变量。

package com.pattern.singleton.s2;public class Sunshine {  private static Sunshine singleton=new Sunshine();    private Sunshine(){    }  public static Sunshine getSunshine(){  return singleton;  } }

初试化静态的singleton创建一次。如果我们在Sunshine类里面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。而降低内存的使用率。缺点是没有延迟加载lazy loading的效果,从而降低内存的使用率。

2、如果不希望提前创建单例模式,就延迟初始化,在第一次需要它时再创建实例。这就是单例的第二种做法。

package com.pattern.singleton;public class Sunshine {  private static Sunshine singleton=null;    private Sunshine(){//私有构造方法保证不能让其他地方创造第二个太阳    }  public static Sunshine getSunshine(){  if(singleton==null){  singleton=new Sunshine();  }  return singleton;  } }
如果把上述代码放到多线程的环境中,可能会出现两个或多个线程同时初始化一个单例对象的情况。假设第一个线程发现singleton 为null,紧接着第二个线程运行下拉也发现singleton为null,然后两个线程都会对该对象进行实例化。为了避免多个线程同时初始化对象实例,就必须改进上述单例模式设计方法,加入同步机制,实现多线程协调运行。

3、最简单的方式,就是在方法中加入synchronized实现线程同步。

package com.pattern.singleton.s1;public class Sunshine {  private static Sunshine singleton=null;    private Sunshine(){//私有构造方法保证不能让其他地方创造第二个太阳    }  public static synchronized Sunshine getSunshine(){  if(singleton==null){  singleton=new Sunshine();  }  return singleton;  }   }
加上了同步锁,使得在多线程的情况下可以用。例如:当两个线程同时想创建实例,由于在一个时刻只有一个线程能得到同步锁,当第一个线程加上锁以后,第二个线程只能等待。第一个线程发现实例没有创建,创建之。第一个线程释放同步锁,第二个线程才可以加上同步锁,执行下面的代码。由于第一个线程已经创建了实例,所以第二个线程不需要创建实例。保证在多线程的环境下也只有一个实例。但是每次通过getSunshine方法得到singleton实例的时候都有一个试图去获取同步锁的过程。加锁是很耗时的,效率太低,所以可以考虑将加锁的过程考虑到方法内。

4、这就出现了双重校验锁的单例模式

package com.pattern.singleton.s1;public class Sunshine {  private static Sunshine singleton=null;    private Sunshine(){//私有构造方法保证不能让其他地方创造第二个太阳    }  public static Sunshine getSunshine(){  if(singleton==null){  synchronized (Sunshine.class) {    if(singleton==null){  singleton=new Sunshine();  }  }  }  return singleton;  }  }
只有当singleton为null时,需要获取同步锁,创建一次实例。当实例被创建,则无需试图加锁.缺点是用双重if判断太复杂,容易出错。

那么有没有一种方式,既可以做到延迟加载,又可以避免双重锁的机制呢?

5、匿名内部类-单例模式

package com.pattern.singleton.s1;public class Sunshine {  private Sunshine(){//私有构造方法保证不能让其他地方创造第二个太阳    }  private static class SunshineFactory{  private final static Sunshine singleton=new Sunshine();  }  public static Sunshine getSunshine(){  return SunshineFactory.singleton;  } }
定义一个私有的内部类,在第一次用这个嵌套类时,会创建一个实例。而类型为SunshineFactory的类,只有在Sunshine.getSunshine()中调用,由于私有的属性,他人无法使用SunshineFactory创建新实例。


源码下载:http://download.csdn.net/download/pelifymeng2/9994100

原创粉丝点击