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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 杨鹏鹏 中级黑马   /  2012-8-19 12:26  /  1901 人查看  /  7 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

package com.itheima;

public class Singleton {
   private static Singleton instance=null;
   public static  Singleton getInstance(){
           if(instance==null){
                   instance=new Singleton();
                  
           }
           return instance;
   }
}
为什么说这种单例模式不安全?求解

7 个回复

倒序浏览
这是单例吗?构造方法要私有,否则程序外我这样Singleton sl = new Singleton()也新建了一个对象
回复 使用道具 举报
懒汉式需要同步下,把获取引用的函数变成同步函数吧,否则有可能产生多线程安全问题
回复 使用道具 举报
public static  Singleton getInstance(){
           if(instance==null){//可能出现的情况:线程A进入,判断非null,判断完之后,还没有等到执行  instance=new Singleton()语句,
                                      //执行权就到线程B,然后B进入,判断非null,执行  instance=new Singleton(),过一段时间
                                       //线程A夺回执行权,也执行  instance=new Singleton(),就导致创建的 instance不唯一


                   instance=new Singleton();
                  
           }
           return instance;
   }

这是我的学习心得,当日记写的,分享下:

/*该懒汉式的缺点是:程序每次访问的时候都要判断一次锁,十分浪费资源,使程序低效*/
class Single{
         private static Single s = null;
         private Single(){}
         public static synchronized Single getInstance(){
                   if(s == null)
                            s = new Single();
                   return s;
         }
}

//下面懒汉式提高效率
class Single1{
         private static Single1 s = null;
         private Single1(){}
         //利用双重循环来判断,提高了效率
         public static  Single1 getInstance(){
                   if(s ==null){
                            synchronized(Single1.class){
                                     if(s==null){
                                               s = new Single1();
                                     }
                            }
                   }
                   return s;
         }
}
回复 使用道具 举报
本帖最后由 方志亮 于 2012-8-19 13:29 编辑

单例设计模式的两种方式
                A:饿汉式 当类加载的时候,就创建对象。
                        
                class Student
                {
                        private Student(){}
                        
                        private static Student s = new Student();
                        
                        public static Student getInstance()
                        {
                                return s;
                        }
                }

                B:懒汉式 当使用的使用,才去创建对象,线程安全的。

                class Student
                {
                        private Student(){}

                        private static Student s = null;
                        
                        public static synchronized  Student getInstance()
                        {
                                if(s==null)
                                {
                                        //线程1就进来了,线程2就进来了。
                                        s = new Student();
                                }
                                return s;
                        }
                }
回复 使用道具 举报
1).单例需要将构造方法私有化,不然可以new对象的话,单例就不是单例了
2).你得代码在单例里面叫懒汉式:优点是可以延迟加载,缺点是会产生线程安全问题
例,有2个线程同时访问getInstance方法,分别为线程1和线程2
线程一开始执行并执行到
  1.    public static  Singleton getInstance(){
  2.            if(instance==null){
  3.                   // 线程1执行到这里,CPU交给线程2继续执行
  4.                    instance=new Singleton();
  5.                   
  6.            }
  7.            return instance;
  8.    }
复制代码
÷πºø
这时线程交给线程2执行,线程2一下就执行完了getInstance方法,这时候instance已经引用了一个对象,
但是线程1并没有执行完,CPU将执行权交给了线程1,线程1接着刚才暂停地方继续执行,此时instance又被赋了一遍值
这会造成,线程1和2获得单例不一样,而且多产生一个对象会浪费资源
回复 使用道具 举报
当有两个或者两个以上的线程同时进入到 if(instance==null)中,显然此时intance是为null的。所以两个线程的if的判断语句都为真,所以都可以进入到if语句中。所以都会执行new语句。张忠豹和方志亮为正解。
回复 使用道具 举报
这个涉及到后面的多线程,这个问题以前我回答过,现在再说一遍。顺便加上后面多线程的理解。
现在这个过程形象点说:
先说判断两次的流程。A程序调用方法,判断instance==null,成立进来了(假如此时cpu切换了,A挂起,另一个程序B进来,继续执行,一个道理。总体说,谁先上锁,谁就进来。只会有一个程序进来。)继续说A程序,进来以后,上锁,此时如果挂起,其它程序已经进不来了,等待cpu切换回来,A继续执行,此时判断instance ==null'依然成立,接着就创建对象,然后返回instance 。这时就算B重新被执行进来以后instance 的值已经不为空,也就不会再建立对象,这样就实现了对象的唯一性。
假如就判断一次,如果此时A程序调用方法判断条件成立进来了,然后挂了,B程序也进来了,没挂,锁门,建立对象,走了。A程序又活了,然后锁门,又建立对象,这就失去单例设计模式的意义了。

判断两次就是怕一个程序进来了,挂了,另一个程序也进来了,执行上锁后,就能保证判断第二次条件的时候只有一个程序,然后只创建一个对象。当其它程序再进来的时候,条件不再成立,就不会再创建对象了。


同步方式1:同步函数,就是把同步关键字加在函数上,这种方式简单,但是效率较低。在线程较多 同时访问的时候,都会判断这个锁是否锁上。当进去以后,发现instance不为空了,白等半天。
public static synchronized Single getInstance() {

            if( instance ==null)
            instance = new Single();                        

        return  instance ;
}

另一方式2:这个方式,跟上边同理,每一个线程都会判断锁,降低了效率。
public static Single getInstance()         {
                         synchronized(Single.class)
                         {
                                 if( instance ==null)
                                  instance = new Single();                        
                        }
                 return  instance ;
         }

终极代码:
public static Single getInstance()         {
                 if(instance==null)
                 {
                         synchronized(Single.class)
                         {
                                 if( instance ==null)
                                  instance = new Single();                        
                        }
                 }
                 return  instance ;
         }
这种是效率较高 而且安全的做法。因为当有一个线程,创建完对象了,其他线程进来以后 只需要判断if语句就ok了,不成立,就走了,而不会再去判断锁。
这些学到后面的多线程的synchronized关键字,你对这个的原理就知道了。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马