A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 李伟 中级黑马   /  2012-6-15 15:05  /  1706 人查看  /  2 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

单例设计模式:解决一个类在内存中只存在一个对象,保证对象在内存中的唯一性

1.为了避免其他程序过多建立该对象,先禁止其他程序建立该对象

2.为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象

3.为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式



在实际应用中,对于事物该怎么描述就怎么描述,当需要保证该事物的对象在内存中的唯一性,只要加上以上三步就可以了。



一、单例设计模式之一:饿汉式(类一进内存就建立对象)

class Singe

{

        private Single(){}//禁止其他程序建立对象

        private static Single s=new Single();//自定义一个对象

        public static Single getInstance()//对外提供访问方式获取自定义对象

        {

                return s;

        }

        //其他属性和功能照常

}



二、单例设计模式之二:懒汉式(类刚进内存时对象还没有建立,只有获取对象的方法被调用时才建立对象)

class Singe

{

        private Single(){}//禁止其他程序建立对象

        private static Single s=null;//定义一个空对象

        public static Single getInstance()//对外提供访问方式获取自定义对象

        {

               if(s==null)

                       s=new Single();//当该方法被调用时,建立对象

                return s;

        }

        //其他属性和功能照常

}

但是这种方式很不安全,当有两个或两个以上的程序访问该方法时,CPU执行的过程中会不断的在各个程序间切换执行,容易在某段时间内造成多个程序卡在if(s==null)和s=new Single();之间,导致最后在堆内存中创建了多个对象,保证不了对象在内存中的唯一性,就有了安全隐患。

为了解决懒汉式的这个安全隐患,有两种方式:

1.给获取对象的方法上锁
      public static synchronized Single getInstance()//加上sychronized关键字,给方法上锁
      {
               if(s==null)
                       s=new Single();//当该方法被调用时,建立对象
                return s;
          }
当一个程序正在执行该方法的时候,其他程序进不来,执行完毕其他程序才进得来,从而保证了对象在内存中的唯一性,但是这种解决方式效率很低。

2.在判断语句内读锁
        public static Single getInstance()
        {
               if(s==null)
               {
                      synchronized(Single.class)//进行读锁
                      {
                              if(s==null)
                              s=new Single();//当该方法被调用时,建立对象
                      }
               }
                              return s;
        }

这种方式也保证了对象在内存中的唯一性。

但是不太理解这种解决方式和第一种解决方式有什么区别吗,能解决第一种解决方式效率低的问题吗?

评分

参与人数 1黑马币 +20 收起 理由
黄奕豪 + 20 赞一个!

查看全部评分

2 个回复

倒序浏览
第二种方法并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
第二种方法的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
这种实现方式可以实现既线程安全地创建实例,而又不会对性能造成太大的影响。它只是在第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。

回复 使用道具 举报
饿汉式:因为s被定义为静态的,所以类一加载,就会new出对象,无论有没有使用,这个对象一直存在,比较占用内存空间。
懒汉式:当只用要调用这个类的对象时,才去判断s是不是等于null,如果等于,就创建一个对象,如果不等于,就不创建,表面上看是只能创建一个对象,但是我们了解CPU的运行机制后会明白,在某种情况下,会出现第一个调用者刚好判断完if(s==null)这个语句,CPU切换到下一个调用者,这时候两个调用者的判断都通过了,就会new出两个对象,显然就违背了我们单例的初衷。
于是就有后面两种加锁的写法,也就是一个调用者在判断语句到new对象语句完成之前,其他调用者想调用都需要先等待,这样就避免了懒汉式的缺点,又能在不调用的时候不new对象,节约内存!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马