单例模式——Singleton

单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。正是由于这个特 点,单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或 文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境 下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。

1.最简单的实现方式如下:

public class Singleton {    
      //用一个静态变量来记录Singleton类的唯一实例   
    private static Singleton uniqueInstance;   
    
      private Singleton() {}   
           
      //注意这个方法也是静态的   
      public static Singleton getInstance() {    
           if(uniqueInstance == null) {   
             uniqueInstance = new Singleton();   
           }   
           return uniqueInstance;   
      }   
}  

 

上述实现方式,在单线程时是没有问题的,但是在多线程时,便会产生线程安全问题了.如果第一个线程发现了成员变量现在为空的时候,准备创建对象时,第二个线程也发现了成员变量为空,也会继续再创建一个对象,这样就会造成JVM中有多个单例类型的实例。而如果这个单例类型的成员变量在运行过程中变化,将会造成多个单例类型实例的不一致,产生一些很奇怪的现象。

2.使用synchronized关键字锁定整个方法确保getInstance()方法一次只能被一个线程调用

public class Singleton { 
    //用一个静态变量来记录Singleton类的唯一实例   
   private static Singleton uniqueInstance;   
    
    private Singleton() {}   
           
    //注意这个方法也是静态的   
   public static synchronized Singleton getInstance() {    
        if(uniqueInstance == null) {   
             uniqueInstance = new Singleton();   
         }   
         return uniqueInstance;   
    }   
}  

 

 但很多时候我们通常会认为锁定整个方法的是比较耗费资源的,代码中实际会产生多线程访问问题的只有 instance = new Singleton(); 这一句,为了降低 synchronized 块性能方面的影响,只锁定instance = new Singleton();

public class Singleton {    
  
    private volatile static Singleton uniqueInstance;   
    
    private Singleton() {}   
           
    public static Singleton getInstance() {    
    if(uniqueInstance == null) { //(1)   
        //只有第一次才彻底执行这里的代码   
       synchronized() {   
          //再检查一次   
          if(uniqueInstance == null)   
        uniqueInstance = new Singleton();   
       }   
    }   
         return uniqueInstance;   
    }   
}  

 同时也可以使用急切创建实例,而不用延迟实例化的做法:

public class Singleton {    
  
   private static Singleton uniqueInstance = new Singleton();  
    
    private Singleton() {}   
           
    public static Singleton getInstance() {    
         return uniqueInstance;   
    }   
}  

 private static Singleton uniqueInstance = new Singleton();  在静态初始化器(static initializer)中创建单例,这保证了线程安全。利用这个做法,JVM在加载这个类时马上创建此唯一的单件实例。JVM保证任何线程访问uniqueInstance静态变量之前,一定先创建些实例。

 还有一种方式则更加灵巧,没有使用同步但保证了只有一个实例,还同时具有了Lazy的特性:

public class ResourceFactory {      
    private static class ResourceHolder {      
        public static Resource resource = new Resource();      
     }      
     
    public static Resource getResource() {      
        return ResourceFactory.ResourceHolder.resource;      
     }      
     
    static class Resource {      
     }      
}    

 上面的方式是值得借鉴的,在ResourceFactory中加入了一个私有静态内部类ResourceHolder ,对外提供的接口是 getResource()方法,也就是只有在ResourceFactory .getResource()的时候,Resource对象才会被创建,这种写法的巧妙之处在于ResourceFactory 在使用的时候ResourceHolder 会被初始化,但是ResourceHolder 里面的resource并没有被创建,这里隐含了一个是static关键字的用法,使用static关键字修饰的变量只有在第一次使用的时候才会被初始化,而且一个类里面static的成员变量只会有一份,这样就保证了无论多少个线程同时访问,所拿到的Resource对象都是同一个。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值