单例模式

举报
yd_221104950 发表于 2020/12/02 23:18:39 2020/12/02
【摘要】 有一些对象其实我们只需要一个就够了,比如线程池、缓存、对话框、处理偏好设置、注册表的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。如果制造出多个,反而会导致许多问题。 单例模式定义 确保一个类只有一个实例,并提供一个全局访问点。 经典的单例模式实现 第一步:利用一个静态变量来记录Singleton类的唯一实例。 第二步:把构造器声明为私有的,只有在Si...

有一些对象其实我们只需要一个就够了,比如线程池、缓存、对话框、处理偏好设置、注册表的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。如果制造出多个,反而会导致许多问题。

单例模式定义

确保一个类只有一个实例,并提供一个全局访问点。

经典的单例模式实现

第一步:利用一个静态变量来记录Singleton类的唯一实例。
第二步:把构造器声明为私有的,只有在Singleton类内才可以调用构造器。
第三步:用getInstance()方法返回这个实例。

public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } //其他方法
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

如果多个线程来访问此单例就会有很大的麻烦,有可能在多个线程访问上述单例时会重复创建单例。我们只要把getInstance()变成同步(synchronized)方法,多线程灾难几乎就可以轻易地解决了。通过增加synchronized关键字到getInstance()方法,我们就可以迫使每个线程进入这个方法之前,要先等候别的线程离开该方法,即不会有两个线程同时进入这个方法,如:

public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

同步getInstance方法既简单又有效,但是同步一个方法可能会造成程序执行效率下降100倍。我们可以用“双重检查加锁”,在getInstance()中减少使用同步来解决。首先检查是否实例已经创建了,如果实例未创建,才进行同步。这样一来,只有第一次会同步:

public class Singleton { private volatile static Singleton instance; private Singleton() { } public static  Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } //其他方法
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

通过这样做后就可以大大减少getInstance的时间耗费。
说明:volatile修饰记录实例的静态变量instance,可以确保instance被初始化成Singleton实例时,多个线程正确地处理instance变量。

在声明静态变量时就初始化

如果应用程序总是创建并使用单例,或者在创建和运行时方面的负担不太繁重,可以用下面这种方式:

public class Singleton { private static Singleton instance = new Singleton(); //在静态初始化器中创建单例,这段代码保证了线程安全 private Singleton() { } public static Singleton getInstance() { return instance; } //其他方法
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的单例,JVM保证在任何线程访问instance静态变量之前,一定先创建此实例。

注意:

每个类加载器都定义了一个命名空间,如果有两个以上的类加载器,不同的类加载器可能会加载同一个类,从整个程序来看,同一个类会被加载多次。如果这样的事情发生在单例上,就会产生多个单例并存的怪异现象。所以,如果你的程序有多个类加载器又同时使用了单例模式,请小心,解决办法就是自行指定类加载器,并指定同一个类加载器。

关于继承单例类的问题

继承单例类会遇到一个问题,就是构造器是私有的。你不能用私有构造器来扩展类。所以你必须把单例类的构造器改成公开的或受保护的。但是这样一来就不是真正的单例了,因为别的类可以实例化它。

还有另一个问题,单例的实现是利用静态变量,直接继承会导致所有的派生类共享同一个实例变量,这可能不是你想要的。
所以我不建议你这么做。

最后给出GitHub上的demo代码。

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/88606183

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。