Head First设计模式学习笔记第五天

Head first设计模式学习第五天

单例模式:确保一个类只有一个实例,并提供一个全局访问点。

单例模式的好处,可以确保程序中使用的全局资源只有一份。一般用于像线程池或数据库连接上,可以减少因为过多的实例化同一个对象而造成代码运行时的bug。单例模式中构造方法是私有的,但会放出一个公有的静态方法,当调用该静态方法的时候就会调用这个实例,但这个实例有可能是这次调用时创建的,也有可能是以前创建的。代码展示:

publicclass ChocolateBoiler {

      privatestaticbooleanempty;

      privatestaticbooleanboiled;

      privatestatic ChocolateBoiler uniqueInstance;

     

      private ChocolateBoiler(){

           empty=true;

           boiled=false;

      }

      publicstatic ChocolateBoiler getInstance(){

           if(uniqueInstance==null){

                 uniqueInstance=new ChocolateBoiler();

           }

           returnuniqueInstance;

      }

      publicvoid fill() {

           if (isEmpty()) {

                 empty = false;

                 boiled = false;

           }

      }

 

      publicvoid drain() {

           if (!isEmpty() && isBoiled()) {

                 // drain the boiled milk and chocolate

                 empty = true;

           }

      }

 

      publicvoid boil() {

           if (!isEmpty() && !isBoiled()) {

                 // bring the contents to a boil

                 boiled = true;

           }

      }

 

      publicboolean isEmpty() {

           returnempty;

      }

 

      publicboolean isBoiled() {

           returnboiled;

      }

}

 

直白的讲,把某个类设计成自己管理的一个单独实例,可以同时避免其他类再自行产生实例。要想取得单件实例,只能通过单件类。同时提供这个实例的全局访问点,需要实例时,向类查询,就会返回实例。但上面的例子是利用延迟实例化的方式创建单件,这种做法对资源敏感的对象特别重要。但这样写的话,进行多线程操作时会出现没进行锁操作的一切问题。而只要把getInstance()变成同步(synchronized)方法,问题就解决了,这样就能确保不会有两个及以上的线程进入方法。

但使用同步锁的做法会极大的影响程序的执行效率,甚至有可能拖垮性能,所以一般使用单件模式有三种选择:

1:如果全局访问点也就是上面代码的getInstance()的性能对应用程序不是很关键,就什么都别做。因为同步getInstance()的方法即简单又有效,但如果getInstance()的程序使用在频繁运行的地方时,就可以选择第三种方法,这种模式也就是俗称的懒汉模式;

public class Singleton {

         private static Singleton uniqueInstance;

         private Singleton() {}

         public static synchronized Singleton getInstance() {

                   if (uniqueInstance == null) {

                            uniqueInstance = new Singleton();

                   }

                   return uniqueInstance;

         }

}

 

2:使用“急切”创建实例,而不用延迟实例化的做法。如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,可以使用下面的方法:

publicclass Singleton {

    privatestatic Singleton uniqueInstance=new Singleton();

    private Singleton(){}

    publicstatic Singleton getInstance(){

        returnuniqueInstance;

    }

}

 

在静态初始化器中创建单件,这保证了线程的安全。利用这个做法,依赖JVM在加载这个类时马上创建唯一的单件实例。JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例。但这会给运行内存制造压力,每次调用Singleton时都会先创建uniqueInstance,会造成内存的浪费。而这种做法一般被我们称做“饿汉模式”。


3:用“双重检查枷锁”,在getInstance()中减少使用同步。使用双重检查加锁,首先检查是否实例已经创建,如果尚未创建,“才”进行同步。这样的话只有第一次会同步。具体代码如下:

publicclass Singleton {

    privatevolatilestatic Singleton uniqueInstance;

    private Singleton(){}

    publicstatic Singleton getInstance(){

    if(uniqueInstance==null){

         synchronized (Singleton.class) {

                if(uniqueInstance==null){

                    uniqueInstance=new Singleton();

                }

            }

    }

    returnuniqueInstance;

    }

}

 这样的做法,可以大大减少getInstance()的时间耗费。而这种做法也就是俗称的“饿汉模式懒加载”。

注意:双重检查加锁不适用于JDK1.4及更早版本的Java。因为volatile关键字的实现会导致双重检查加锁的失效。所以使用JDK1.4及更早版本的Java,就请不要利用此技巧实现单例模式。

 

注意:如果使用多个加载器,可能导致单件失效而产生多个实例。因为不同的类加载器可能加载同一个类,从整个程序看,同一个类会被加载多次。如果这样的事发生在单例上,就会产生多个单件并存的现象。所以当程序有多个类加载器又同时使用单件模式时,最好自行指定加载器,并指定同一个类加载器。

 

注意:使用JVM1.2或之前的版本,必须建立单件注册表,以免垃圾收集器将单件回收(这是一个bug,JVM1.3及以后的版本已经修正了)。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值