单例模式(含线程锁关键字)

单例就是只有一个例子,只有一个对象,不允许别人再创建对象。

饿汉式(初始化即创建对象)

class Single{

  private static Single s = new Single();

  private Single(){}

  public static Single getInstance(){

    return s;

  }

}

懒汉式(方法被调用时,才创建对象,也叫做对象的延时加载)

class Single{

  private static Single s = null;

  private Single(){}

  public static Single getInstance(){//当多人同时调用此方法时有可能出状况

    if(s == null)      //语句①

      s = new Single();//语句②

    return s;

  }

}

懒汉式看似省空间,却有可能在多线程时出问题。

举个只有两个线程的例子:线程A被单核CPU执行到①,单核CPU切入线程B去执行①,仍然会通过判断,此时A,B都会执行语句②。

改进后的安全懒汉式(低效,在方法上增加了线程锁):

class Single{

  private static Single s = null;

  private Single(){}

  public synchronized static Single getInstance(){

    if(s == null)      //语句①

      s = new Single();//语句②

    return s;

  }

}

tips:

线程锁就是synchronized后边的参数,汉语版的API中作者称之为对象监视器。线程锁有两个状态,一个锁住一个打开,打开的时候线程就能进去,关闭的时候,线程就会在门前等待,直到锁打开才会进去。 

synchronized相当于一个标示符表示它所跟随的大括号内的内容是同步代码块,执行这部分代码块就要判断线程锁的状态。

再次改进后最终的懒汉式(在方法内部增加线程锁)

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;

  }

}

解析一下:线程A执行语句①通过,执行语句②通过,此时CPU切入线程B执行到语句①通过,执行到语句②未通过,

然后CPU切入线程A继续执行,通过语句③和④并解除线程锁,CPU再次切入线程B,此时会通过语句②,执行语句③,

如果没有语句③又悲剧了。。。

总结,既然有这么一个单例类,肯定你是要用它的,你要用它一定会开辟内存存放它的对象,

懒汉实在是浪费时间又没什么实际意义,所以建议选择饿汉式的单例模式。

原文地址:https://www.cnblogs.com/flying607/p/3418758.html