美文网首页
五分钟精通设计模式(四)--单例模式

五分钟精通设计模式(四)--单例模式

作者: 程序员伟哥 | 来源:发表于2018-07-07 14:59 被阅读3次

本文由【程序员伟哥】原创,转载请注明出处,不注明请默念三遍【我爱技术】后转载!
更多精彩请关注微信公众号:【IT殿堂】
可以加我微信一起交流技术:【qqqq123456】
都说单例模式简单,可是简单中藏着大大的不简单。
一、介绍
为了避免某个频繁使用的对象不断地重新创建,我们可以使用单例模式。单例模式可以保证在一个JVM中,只有一个实例存在。
二、实现步骤
1、创建单例类
2、在单例类中,声明 private static 的这个类本体类型的变量。
3、只在这个类中保留private的构造方法
4、写一个静态方法,获取这个类被本体类型变量持有的静态变量实例(若为null需要新建)。
如下图所示:
//第一步,创建单例类
public class Singleton {
//第三步、只在这个类中保留private的构造方法
private Singleton(){}
//第二步、在单例类中,声明 private static 的这个类本体类型的变量。
private static Singleton singleton = null;
//写一个静态方法,获取这个类被本体类型变量持有的静态变量实例(若为null需要新建)。
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}

哇塞,单例模式 果然简单,到此万事大吉了!
那么你太天真了!
没错,这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,很可能就出现问题了!
写一个Test类,如下:
/**

  • @Title Test.java
  • @Package com.buckyball.dp.singleton
  • @author Roy
  • @email 574613441@qq.com
  • @weixin qqqq123456
  • @date 2018年6月28日 下午8:09:06
  • @Desc
    */
    package com.buckyball.dp.singleton;

/**

  • @Title Test.java
  • @Package com.buckyball.dp.singleton
  • @author Roy
  • @email 574613441@qq.com
  • @weixin qqqq123456
  • @date 2018年6月28日 下午8:09:06
  • @Desc
    /
    public class Test {
    public static Singleton ssingleton;
    /
    *
    • @param args
      */
      public static void main(String[] args) {

      Thread thread1 = new Thread(new Runnable(){
      @Override
      public void run() {
      System.out.println("thread1 run");
      Singleton singleton = Singleton.getInstance();
      System.out.println("thread1 singleton = "+singleton);
      ssingleton = singleton;
      }
      });
      thread1.setName("thread1");
      Thread thread2 = new Thread(new Runnable(){
      @Override
      public void run() {
      System.out.println("thread2 run");
      Singleton singleton = Singleton.getInstance();
      System.out.println("thread2 singleton = "+singleton);
      ssingleton = singleton;
      }
      });
      thread2.setName("thread2");

      Thread thread3 = new Thread(new Runnable(){
      @Override
      public void run() {
      System.out.println("thread3 run");
      Singleton singleton = Singleton.getInstance();
      System.out.println("thread3 singleton = "+singleton);
      ssingleton = singleton;
      }
      });
      thread3.setName("thread3");

      thread1.start();
      thread2.start();
      thread3.start();
      }

}

连续运行两次运行输出的结果如下:很明显,如果有并发线程同时调用获取单例,那么是否获取到的是同一单例,那么就看天意了!显然作为一个靠谱的程序是不能这么做的。
thread1 run
thread2 run
thread3 run
thread1 singleton = com.buckyball.dp.singleton.Singleton@13c339f
thread2 singleton = com.buckyball.dp.singleton.Singleton@ba8af9
thread3 singleton = com.buckyball.dp.singleton.Singleton@ba8af9

thread1 run
thread2 run
thread3 run
thread2 singleton = com.buckyball.dp.singleton.Singleton@fa0094
thread3 singleton = com.buckyball.dp.singleton.Singleton@fa0094
thread1 singleton = com.buckyball.dp.singleton.Singleton@fa0094
如何解决?
1、对 getInstance 方法加 synchronized 关键字修饰
/**

  • @Title Singleton.java
  • @Package com.buckyball.dp.singleton
  • @author Roy
  • @email 574613441@qq.com
  • @weixin qqqq123456
  • @date 2018年6月28日 下午8:05:57
  • @Desc
    */
    package com.buckyball.dp.singleton;

/**

  • @Title Singleton.java
  • @Package com.buckyball.dp.singleton
  • @author Roy
  • @email 574613441@qq.com
  • @weixin qqqq123456
  • @date 2018年6月28日 下午8:05:57
  • @Desc
    */
    public class Singleton1 {
    private Singleton1(){}
    private static Singleton1 singleton = null;
    public static synchronized Singleton1 getInstance(){
    if(singleton == null){
    singleton = new Singleton1();
    }
    return singleton;
    }
    }

无论运行多少次,妥妥的每次都是一样的
thread1 run
thread3 run
thread2 run
thread1 singleton = com.buckyball.dp.singleton.Singleton1@ba8af9
thread2 singleton = com.buckyball.dp.singleton.Singleton1@ba8af9
thread3 singleton = com.buckyball.dp.singleton.Singleton1@ba8af9

这样写的问题:
synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进:。
于是看到有人推荐这么写:妥妥的告诉你,给你个大大的叉。synchronized不能锁null对象。
public static Singleton2 getInstance() {
if (singleton == null) {
synchronized (singleton) { //这里会报空指针java.lang.NullPointerException
if (singleton == null) {
singleton = new Singleton2();
}
}
}
return singleton;
}
2、直接声明属性的时候就创建对象
public class Singleton3 {
private Singleton3(){}private static Singleton3 singleton = new Singleton3();public static Singleton3 getInstance(){return singleton;}}
输出如下:
thread1 run
thread2 run
thread3 run
thread1 singleton = com.buckyball.dp.singleton.Singleton3@46b10e8e
thread3 singleton = com.buckyball.dp.singleton.Singleton3@46b10e8e
thread2 singleton = com.buckyball.dp.singleton.Singleton3@46b10e8e
源码地址:https://github.com/tianshanaoxue/design-pattern.git
喜欢我的文章,请微信搜索公众账号:【IT殿堂】,会定期更新原创、系列文章
关注后回复【java】、【Android】、【c++】等关键词有惊喜哦。
也可以加我微信:qqqq123456

相关文章

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • 单例模式

    面向对象设计的设计模式(四):单例模式

  • Java四种单例设计模式

    Java中的四种单例模式 单例模式是最容易理解的设计模式之一,介绍Java中单例模式的四种写法。 1.基本单例模式...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

  • 设计模式一、单例模式

    系列传送门设计模式一、单例模式设计模式二、简单工厂模式设计模式三、工厂模式设计模式四、抽象工厂模式 简单单例(推荐...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • 阿里架构师工作十年接触过Java框架设计模式(续上文)

    1-25续 四、单例设计模式(Singleton Pattern) 4.1 介绍 单例模式是一种创建型模式,单例模...

网友评论

      本文标题:五分钟精通设计模式(四)--单例模式

      本文链接:https://www.haomeiwen.com/subject/csiwuftx.html