241°

Singleton Pattern -- 单例模式

    Singleton Pattern -- 单例模式

  单例模式是用来创建一个只能又一个实例的对象。

  单例模式类图如下。

 

 

 

 

  单例模式通用代码(饿汉模式):

public class Singleton {
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> Singleton singleton = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Singleton();
  
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 限制产生多个对象</span>
<span style="color: #0000ff;">private</span><span style="color: #000000;"> Singleton() {}

</span><span style="color: #008000;">//</span><span style="color: #008000;">通过该方法获取实例对象</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> Singleton getInstance() {
    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> singleton;
}

</span><span style="color: #008000;">//</span><span style="color: #008000;">类中其他方法,尽量是static</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> doSomething() {}

}

  延时单例模式(懒汉模式)

public class Singleton {
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> Singleton singleton = <span style="color: #0000ff;">null</span><span style="color: #000000;">;
  
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 限制产生多个对象</span>
<span style="color: #0000ff;">private</span><span style="color: #000000;"> Singleton() {}

</span><span style="color: #008000;">//</span><span style="color: #008000;">通过该方法获取实例对象</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> Singleton getInstance() {
    </span><span style="color: #0000ff;">if</span>(singleton == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
        singleton </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Singleton();
     }
    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> singleton;
}

</span><span style="color: #008000;">//</span><span style="color: #008000;">类中其他方法,尽量是static</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> doSomething() {}

} 

  

  单例模式和线程

    以上两种模式都属于线程不安全,在不是高并发的条件下,基本不会出现问题,但是当并发量增加时可能回出现多个实例,破坏最处当预期。

    为什么会出现这种情况呢?

      如果一个A执行到singleton = new Singleton(),但还没有获得对象(对象初始化时需要时间的),在此同时第二个线程B也在执行,执行到

     (singleton == null)判断,那么线程B判断的结果也为真。于是继续运行下去,线程A获得了一个对象,线程B也获得了一个对象,

      在内存中就出现了两个对象!

   

    1、只要在getInstance() 方法前加 synchronized 关键字就能解决该问题。

public class Singleton {
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> Singleton singleton = <span style="color: #0000ff;">null</span><span style="color: #000000;">;
  
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 限制产生多个对象</span>
<span style="color: #0000ff;">private</span><span style="color: #000000;"> Singleton() {}

</span><span style="color: #008000;">//</span><span style="color: #008000;">通过该方法获取实例对象
</span><span style="color: #008000;">//</span><span style="color: #008000;">通过添加synchronized关键字到getInstance()方法中,迫使每个线程    
</span><span style="color: #008000;">//</span><span style="color: #008000;">访问该方法之前,要等其他线程从该方法中离开。</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">synchronized</span><span style="color: #000000;"> Singleton getInstance() {
    </span><span style="color: #0000ff;">if</span>(singleton == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
        singleton </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Singleton();
     }
    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> singleton;
}

</span><span style="color: #008000;">//</span><span style="color: #008000;">类中其他方法,尽量是static</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> doSomething() {}

} 

  解决了线程安全问题,但是使用同步会降低性能,(如果你的系统能接受getInstance()方法带来的额外性能负担,这样的做法简单有效。)

  同步可能造成执行效率下降100倍。因此,如果getInstance()的程序使用非常频繁就得重新考虑了。

  2、用“双重检查加锁”,在getInstance中减少使用同步。

    利用双重检查加锁(double-checkd locking),首先检查是否有实例已创建,如果尚未创建,  

    “才”进行同步,这样一来只有第一次会进行同步,这正是我们需要的。

public class Singleton {
     //volatile关键字确保:当singleton变量被初始化成
     //Singleton实例时,多个线程正确当处理singleton变量。 
    private volatile static Singleton singleton;
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> Singleton() {}

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> Singleton getInstance() {
    
    </span><span style="color: #0000ff;">if</span>(singleton == <span style="color: #0000ff;">null</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">检查实例,如果不存在就进入同步区块。
        </span><span style="color: #008000;">//</span><span style="color: #008000;">只有第一次才会彻底执行此处代码。</span>
        <span style="color: #0000ff;">synchronized</span> (Singleton.<span style="color: #0000ff;">class</span><span style="color: #000000;">) {
              </span><span style="color: #008000;">//</span><span style="color: #008000;">进入区块再做一次判读,如果位null才会创建。  </span>
             <span style="color: #0000ff;">if</span>(singleton == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
                singleton </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Singleton();
            }    
        }
     }
    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> singleton;
}

</span><span style="color: #008000;">//</span><span style="color: #008000;">类中其他方法,尽量是static</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> doSomething() {}

} 

  如果你关心性能,这种做法可以大大减少getInstance()的耗时。

 

 

注意:双重检查加锁不实用1.4以前的Java。

  Java1.2前会造成,单利在没有全局的引用时被当作垃圾回收掉。Java1.2以后这个问题已被修复了。

 

 

 

    

本文转载自博客园,原文链接:https://www.cnblogs.com/klyjb/p/11493314.html

全部评论: 0

    我有话说: