zoukankan      html  css  js  c++  java
  • 单例模式

    //代码引自Java设计模式,我自己稍有改动
    
    public class Factory{
        private static Factory factory;
        
        private long wipMoves;
    
        private Factory(){
            wipMoves=0;
        }
    
        public static Factory getFactory(){
            synchronized(Factory.class){//这里能不能用this???
                 if(factory==null)
                  factory=new Factory();
               return factory;
            }
        }
    }

    Java中,不能在static 方法中使用this:http://www.cnblogs.com/EvanLiu/archive/2013/06/05/3118420.html

     感觉那个例子不是很好哇,http://my.oschina.net/u/866190/blog/205454这个分析还是比较好的

    很多人熟知单例模式中有一种写法是使用双重检查锁实现的,但是在网上看到的例子基本上都不正确,有些写是正确,但没有很好解析,造成很多人没有真正理解。其中,典型错误写法是这样的:

     1 public class Resource {
     2 
     3     private static Resource resource ;
     4     
     5     public static Resource getInstance(){
     6         
     7         if(resource == null ){
     8             synchronized (Resource.class) {
     9                 if(resource  == null ){
    10                     resource  = new Resource() ;
    11                 }
    12             }
    13             
    14         }
    15         
    16         return resource ;
    17     }
    18     
    19     private Resource(){}
    20 
    21 }

    它基本思路是,首先在没有同步的情况下检查resource是否等于null,如果条件不成立,直接返回resource 。否则,就使用同步再检查resource是否等于null,条件成立才正真初始化resource。这中方式既保证只有一个线程初始化resource,又能做到延时加载。似乎是“鱼和熊掌可兼得“。

    上面程序真正的问题是没有同步的情况下读取共享变量resource,并发的情况下对象的状态值有可能是过期无效的。要解决这个问题也很简单,把resource声明为volatile类型。volatile有什么作用?引用《java并发编程实战》的解析:

    当一个域声明为volatile类型后,编译器与运行时会监视这个变量:它是共享的,而且对它的操作不会与其他的内存操作一起被重排序。
    volatile变量不会缓存在寄存器或缓存在对其他处理器隐藏的地方。所以,读一个volatile类型的变量时,总会返回由某一线程所写入的最新值。

    读取volatile变量比读取非volatile变量的性能几乎没有差别,不过需要注意的是volatile只能保证内存可见性,并不能保证原子性。

    所以嘛,这个是双重检查锁中最好的代码(代码引自维基百科)

     1   public class Singleton {
     2     private static volatile Singleton INSTANCE = null;
     3  
     4     // Private constructor suppresses 
     5     // default public constructor
     6     private Singleton() {}
     7  
     8     //thread safe and performance  promote 
     9     public static  Singleton getInstance() {
    10         if(INSTANCE == null){
    11              synchronized(Singleton.class){
    12                  //when more than two threads run into the first null check same time, to avoid instanced more than one time, it needs to be checked again.
    13                  if(INSTANCE == null){ 
    14                      INSTANCE = new Singleton();
    15                   }
    16               } 
    17         }
    18         return INSTANCE;
    19     }
    20   }

     维基百科真好,再看看这个:http://zh.wikipedia.org/wiki/%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F#Java.E4.B8.AD.E7.9A.84.E4.BD.BF.E7.94.A8

    (维基百科,饿汉方式)

      public class Singleton {
        private final static Singleton INSTANCE = new Singleton();
     
        // Private constructor suppresses   
        private Singleton() {}
     
        // default public constructor
        public static Singleton getInstance() {
            return INSTANCE;
        }
      }

    http://www.cnblogs.com/techyc/p/3529983.html中说

    根据Java Language Specification,JVM本身保证一个类在一个ClassLoader中只会被初始化一次。那么根据classloader的这个机制,我们在类装载时就实例化,保证线程安全。

    但是,有些时候,这种创建方法并不灵活。例如实例是依赖参数或者配置文件的,在getInstance()前必须调用某些方法设置它的参数。

    http://www.cnblogs.com/techyc/p/3529983.html中也说了单例模式失效的场景

    http://blog.csdn.net/haoel/article/details/4028232中说明对失效情况进行了分析

    Effective Java作者Josh Bloch 提倡的是用枚举

    http://www.importnew.com/6461.html这个写的好:

    1 public enum Singleton{
    2     INSTANCE;
    3 }

    很神奇,关于枚举《effective java》中有介绍,还有这个链接:http://www.ibm.com/developerworks/cn/java/j-lo-enum/

    还有其他方法,参看:

    http://zh.wikipedia.org/wiki/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F

    http://blog.csdn.net/haoel/article/details/4028232

    http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html

    通常单例模式在Java语言中,有两种构建方式:(维基百科)

    • 懒汉方式(延迟加载)。指全局的单例实例在第一次被使用时构建。
    • 饿汉方式(非延迟加载)。指全局的单例实例在类装载时构建。

    双重检查锁定模式首先验证锁定条件(第一次检查),只有通过锁定条件验证才真正的进行加锁逻辑并再次验证条件(第二次检查)。(维基百科)

    说了这么多,还不知道什么情况下使用单例模式呢,如果让举个例子,怎么说呢

    http://blog.csdn.net/csh624366188/article/details/7465505看看这个,虽然不是我想要的结果

    登记式单例类 登记式单例类是GoF 为了克服饿汉式单例类及懒汉式单例类均不可继承的缺点而设计的。(http://www.javaarch.net/jiagoushi/557.htm,博主应该是阿里的人,先膜拜一把)“饿汉式单例类及懒汉式单例类均不可继承”这句话不懂唉,还说“java.lang.Runtime 典型的单例模式”

    这些下次看这个时再找资料补充吧

    还有一种方法也比较好,是用静态内部类实现的:http://www.cnblogs.com/coffee/archive/2011/12/05/inside-java-singleton.html

     1 public class Singleton {
     2 
     3     private Singleton() {
     4 
     5     }
     6     
     7     private static class SingletonHolder {
     8         private static Singleton instance = new Singleton();
     9     }
    10 
    11     public static Singleton getInstance() {
    12         return SingletonHolder.instance;
    13     }
    14 }

    解释一下,因为java机制规定,内部类SingletonHolder只有在getInstance()方法第一次调用的时候才会被加载(实现了lazy),而且其加载过程是线程安全的(实现线程安全)。内部类加载的时候实例化一次instance。

     这个博客写的好 : http://www.chenyudong.com/archives/java-singleton.html#i-2

  • 相关阅读:
    keras_12_keras自带的Applications
    keras_11_keras中示例数据集
    keras_10_回调函数 Callbacks
    Runloop
    SDWebImage
    NSOperation
    单例模式
    GCD
    一文读懂汉明码
    聊聊SPOOLing技术
  • 原文地址:https://www.cnblogs.com/crane-practice/p/3660119.html
Copyright © 2011-2022 走看看