黑马程序员技术交流社区

标题: 单例模式 懒汉式为什么不安全? [打印本页]

作者: 张先庆    时间: 2013-7-23 21:23
标题: 单例模式 懒汉式为什么不安全?
本帖最后由 杨兴庭 于 2013-7-23 23:23 编辑

class Single
{
private Single(){}
private static Single s = null;
public static Single getInstance(){
   //判断一下,如果s依然是null,建立一个对象
   if(s == null)
  s = new Single();
   return s;
}
}


作者: xscn    时间: 2013-7-23 21:32
本帖最后由 xscn 于 2013-7-23 21:38 编辑

从线程安全性上讲,不加同步的懒汉式是线程不安全的。
比如,有两个线程,一个是线程A,一个是线程B,它们判断条件满足都进来了,它们同时调用getInstance方法,那就可能导致并发问题。
当A、B线程并发的情况下,会创建出两个实例来,这样单例的控制在并发情况下就失效了。


懒汉式一般要用双重检查加锁判断的,视频里有讲
  1. class Single
  2. {
  3.         private static Single s = null;
  4.         private Single(){}
  5.         public static Single getInstance()
  6.         {
  7.                 if(s==null)//检查实例是否存在,如果不存在进入下面的synchronized代码块
  8.                 {
  9.                         synchronized(Single.class)
  10.                         {                               
  11.                                 if(s==null)//拿到锁后再次判断实例是否存在,如果不存在才创建实例
  12.                                         s = new Single();
  13.                         }
  14.                 }
  15.                 return s;
  16.         }
  17. }
复制代码

作者: hx32    时间: 2013-7-23 21:34
在多线程即楼上所说同步状态下会不安全,你先放下这个问题,看到多线程就明白了
作者: 曹奎    时间: 2013-7-23 21:37
这个跟cpu的切换有关系,不加同步的懒汉式不安全,加了同步的懒汉式效率不高!楼上正解
作者: ☆今☆    时间: 2013-7-23 21:37
if(s == null)
  s = new Single();
如果多个线程在判断完 (s == null)为true都在s = new Single();之前给sleep()了,那么醒来后会直接创建对象,而不是重新判断.
导致了创建不止一个的对象.这就是懒汉式的不安全.
作者: 康大玮    时间: 2013-7-23 21:47
1,懒汉式:
       public class Singleton{
            private static Singleton singleton = null;
            public static synchronized synchronized getInstance(){
                 if(singleton==null){                         1
                     singleton = new Singleton();       2
                 }
                return singleton;
            }
       } 2,     毕老师的原话:多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
        cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。这就是多线程的一
       个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。
3,假设一个线程运行到 “1“那行代码它的执行权没了,这是又进来另一个线程,执行到”2“行代码处,此时它也没有执行权了,注意此时已经new
      了一个实例,那么该第一个线程运行了,它接着运行”2“行代码,此时就有了两个实例,它就不能保证是单例了。

作者: 王瀛    时间: 2013-7-23 22:01
懒汉式原型是使用同步方法,但是这样会降低效率,于是使用双重if语句+synchronized代码块改进。

因为单if判断有可能会在内存中产生多于一个的对象,这样就与使用单例模式的初衷不符了。

另外,可以使用枚举的方式实现单例(但是不推荐,开发中常用的还是饿汉式),代码比较简单
  1. enum Single{
  2. INSTANCE;
  3. }
复制代码

作者: 周骑骏    时间: 2013-7-23 22:03
因为当一个线程运行到s为空的时候,他的执行权被抢走了,另外一个线程也运行到s为空这一步,这样的话两个线程都能获取实例。就会导致异常发生
作者: の放下执著    时间: 2013-7-23 22:17
因为s是共享数据,当多个线程并发访问getInstance()方法时,就会有多条线程同时在操作共享数据s,这种情况是有可能发生的。比如:现在有两个线程A,B。线程A调用getInstace()方法,这时线程A已经进入getInstace()方法内,读 if(s == null)语句,突然之间就挂起了(这时有可能的哦)。然后线程B获得了执行权,调用getInstace()方法,这时线程B也进入getInstace()方法内,读 if(s == null)语句,执行s = new Single();于是就在堆内存中创建了对象S。当线程A醒来后,也执行s = new Single();于是就又创建了一个S!!!以此类推,当很多个线程同时操作s时,有可能会new出很多个对象,这就是懒汉式的不安全性。
修改后的代码:
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance(){
   if(s == null)
   {Synchronized(Single.class)
    { if(s == null)
       s = new Single();
     }
    }
   return s;
}
}
作者: 王广亚    时间: 2013-7-23 23:18
这个牵涉到cpu的问题,仔细看看毕老师的视频教程,讲的很清楚




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2