JAVA中单例模式的几种实现方式

1 线程不安全的实现方法

首先介绍java中最基本的单例模式实现方式,咱们能够在一些初级的java书中看到。这种实现方法不是线程安全的,因此在项目实践中若是涉及到线程安全就不会使用这种方式。可是若是不须要保证线程安全,则这种方式仍是不错的,由于所须要的开销比较小。下面是具体的实现代码:java

 

public Class Singleton
{
  private static Singleton instance = null;
  private Singleton(){}
  public  static Singleton getInstance()
  {
     if( instance == null)
        instance = new Singleton ();
     return instance;
  }
}    

 

咱们说过这种实现方式不是thread-safe的,那么能够把上面的方法变成线程安全的吗?固然能够,在方法getInstance()上加上synchronized修饰符就能够实现方法的同步了。可是这样系统开销会很大。具体代码以下:程序员

 

public Class Singleton
{
  private static Singleton instance = null;
  private Singleton(){}
  public  static synchronized  Singleton getInstance()
  {
     if( instance == null)
        instance = new Singleton ();
     return instance;
  }
}    

每次有线程调用getInstance()方法,都须要同步判断。这显然不是最好的选择,下面将会陆续介绍几种thread-safe的方法。安全

 

2 两种lazy loaded thread-safe的单例模式实现方式

1) DCL (double checked locking 实现法)

    double checked locking ,顾名思义,就是双检查法,检查实例INSTANCE是否为null或者已经实例化了。下面是具体的实现代码:多线程

 1 public class DoubleCheckedLockingSingleton{
 2      private volatile DoubleCheckedLockingSingleton INSTANCE;
 3  
 4      private DoubleCheckedLockingSingleton(){}
 5  
 6      public DoubleCheckedLockingSingleton getInstance(){
 7          if(INSTANCE == null){
 8             synchronized(DoubleCheckedLockingSingleton.class){
 9                 //double checking Singleton instance
10                 if(INSTANCE == null){
11                     INSTANCE = new DoubleCheckedLockingSingleton();
12                 }
13             }
14          }
15          return INSTANCE;
16      }
17 }

这种方法也很好理解,咱们能够看到有两次对instance是否为null的判断:若是第一次判断不为空,则直接返回实例就能够了;若是instance为空,则进入同步代码块再进行null值判断,再选择是否实例化。第一个null判断能够减小系统的开销。在实际项目中作过多线程开发的都应该知道DCL。性能

 

2) lazy initialization holder class 模式实现法

下面是这种方法的实现代码:spa

public class Singleton {
    /**
     * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
     * 没有绑定关系,并且只有被调用到才会装载,从而实现了延迟加载
     */
    private static class SingletonHolder{
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static Singleton instance = new Singleton();
    }
    /**
     * 私有化构造方法
     */
    private Singleton(){
    }
    public static  Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

 

当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,致使SingletonHolder类获得初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而建立Singleton的实例,因为是静态的域,所以只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。这个模式的优点在于,getInstance方法并无被同步,而且只是执行一个域的访问,所以延迟初始化并无增长任何访问成本。
线程

 

关于延迟初始化(lazy loadedcode

“除非绝对必要,不然就不要延迟初始化”。延迟初始化是一把双刃剑,它下降了初始化类或者建立实例的开销,却增长了访问被延迟初始化的域的开销,考虑到延迟初始化的域最终须要初始化的开销以及域的访问开销,延迟初始化实际上下降了性能。对象

 

3 静态工厂实现法

  由于单例是静态的final变量,当类第一次加载到内存中的时候就初始化了,其thread-safe性由JVM来负责保证。值得注意的是这个实现方式不是lazy-loadedd的。   具体实现代码以下:blog

 1 public class Singleton{
 2     //initailzed during class loading
 3     private static final Singleton INSTANCE = new Singleton();
 4  
 5     private Singleton(){}
 6  
 7     public static Singleton getSingleton(){
 8         return INSTANCE;
 9     }
10 }

 

枚举实现单例(Enum Singleton)

  枚举单例(Enum Singleton)是实现单例模式的一种新方式,枚举这个特性是在Java5才出现的,在《Effective Java》一书中有介绍这个特性。下面是这种方法的具体实现代码:

public enum Singleton {  
    INSTANCE("hello") {  
        public void someMethod() {  
            // . . .  
        }  
    };  
    private String name;
    private void PrintName(){System.out.println(name);}
    protected abstract void someMethod();  
} 

你能够经过Singleton.INSTANCE来访问该单示例变量。默认枚举实例的建立是线程安全的,可是在枚举中的其余任何方法由程序员本身负责。若是你正在使用实例方法,那么你须要确保线程安全(若是它影响到其余对象的状态的话)。传统单例存在的另一个问题是一旦你实现了序列化接口,那么它们再也不保持单例了,可是枚举单例,JVM对序列化有保证。枚举实现单例的好处:有序列化和线程安全的保证,代码简单

相关文章
相关标签/搜索