经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
23种设计模式入门 -- 单例模式
来源:cnblogs  作者:Weihaom  时间:2021/1/11 9:36:58  对本文有异议

单例模式:采用一定的方法,使得软件运行中,对于某个类只能存在一个实例对象,并且该类只能提供一个取得实例的方法。

分类

  • 饿汉式
    1. 静态常量方式
    2. 静态代码块方式
  • 懒汉式
    1. 普通方式,线程不安全
    2. 同步方法方式,线程安全
    3. 同步代码块方式,线程不安全
  • 其他方式
    1. 双重检查
    2. 静态内部类
    3. 枚举

实现思路:

  1. 想要实现单例,即不能让外部随意的去实例化对象。所以需要构造器私有

  2. 既然不允许外部去创建了,所以需要在类的内部创建对象

  3. 外部需要使用对象,所以需要对外提供一个获取实例的方法

一、饿汉式

根据对象创建的时机,可以分为饿汉式和懒汉式。饿汉式,即在类加载的时候立即创建实例,根据所学知识我们可以很快想到要使用static关键字将属性和类相关联。以下提供两种书写方式供参考。

特点

  • 优点:写法简单,在类装载的时候就完成实例化,避免了线程同步问题
  • 缺点:在类装载的时候就完成了实例化,如果开始至终都没有使用这个实例,会造成内存浪费

1.静态常量方式

  1. class Singleton01{
  2. //构造器私有,防止外部new
  3. private Singleton01(){
  4. }
  5. //内部创建对象
  6. private final static Singleton01 instance = new Singleton01();
  7. //提供给外部创建实例的静态方法
  8. public static Singleton01 getInstance(){
  9. return instance;
  10. }
  11. }

2.静态代码块方式

  1. class Singleton02{
  2. //构造器私有,防止外部new
  3. private Singleton02(){
  4. }
  5. //内部创建对象
  6. private static Singleton02 instance;
  7. static {
  8. instance = new Singleton02();
  9. }
  10. //提供给外部创建实例的静态方法
  11. public static Singleton02 getInstance(){
  12. return instance;
  13. }
  14. }

这种方式和静态常量的实现方式逻辑和优缺点是一样的,只是写法不同。

二、懒汉式

懒汉式,即在类加载时并不实例化对象,等到使用对象实例的时候才去实例化,也被称为懒加载效果。这样做的好处可以节约资源,减少浪费,只有需要的时候采取创建,不需要就不会实例化。

1.普通方式(线程不安全)

  1. class Singleton01{
  2. // 构造器私有
  3. private Singleton01(){
  4. }
  5. // 定义静态变量,实例化留在获取实例的getInstance方法,起到懒加载效果
  6. private static Singleton01 instance;
  7. public static Singleton01 getInstance(){
  8. // 判断如果为空才创建,起到懒加载
  9. if (instance == null){
  10. instance = new Singleton01();
  11. }
  12. return instance;
  13. }
  14. }

问题:在多线程情况下,假设类还未第一次实例化,此时两个进程同时执行到了if(instance==null),而未来得及往下执行时,此时两者校验都成立,都会执行实例化操作,将有可能出现创建多个实例的问题。

2.同步方法方式(线程安全)

既然存在线程安全问题,肯定会想到使用synchronized关键字来解决

  1. class Singleton02{
  2. private static Singleton02 instance;
  3. private Singleton02(){
  4. }
  5. //使用synchronized关键字来实现线程安全
  6. public static synchronized Singleton02 getInstance(){
  7. if (instance == null){
  8. instance = new Singleton02();
  9. }
  10. return instance;
  11. }
  12. }

这样的方式可以起到线程安全的效果,但是每个线程都需要等待锁,所以又会存在效率低的问题,于是有人想到了将锁的范围缩小到方法的内部,使用同步代码块的方式

3.同步代码块方式(线程不安全)

这样的方式好不好呢?先看代码

  1. class Singleton03{
  2. private static Singleton03 instance;
  3. private Singleton03(){
  4. }
  5. public static Singleton03 getInstance(){
  6. if (instance == null){
  7. // 这里的synchronized其实没有实际意义,可能会产生多个实例
  8. synchronized (Singleton03.class){
  9. instance = new Singleton03();
  10. }
  11. }
  12. return instance;
  13. }
  14. }

这样锁的范围是变小了,但是还会存在多个线程同时判断到if (instance == null),即使在后面加上锁,依旧会在后续创建实例,只是延迟了一点而已,所以这种写法不可取

三、其他方式

1.双重检查

为了能够实现懒加载的效果,同时兼顾效率,于是出现了这种写法

  1. class Singleton01{
  2. //volatile,当有发生变化时即时储存到内存中。防止指令重排
  3. private static volatile Singleton01 instance;
  4. private Singleton01(){
  5. }
  6. //双重检查,解决线程同步问题,又保证效率
  7. public static Singleton01 getInstance(){
  8. if (instance == null){ // 第一次检查,降低产生锁的概率
  9. synchronized (Singleton01.class){
  10. if(instance == null){ // 第二次检查,保证线程安全
  11. instance = new Singleton01();
  12. }
  13. }
  14. }
  15. return instance;
  16. }
  17. }

使用双重检查,第一次检查提升效率,第二次检查保证线程安全,简直美滋滋

2.静态内部类

利用静态内部类在被调用时才会加载,即存在懒加载效果,所以也可以这样写

  1. class Singleton02{
  2. private Singleton02(){
  3. }
  4. /*
  5. 静态内部类在外部类装载的时候不会马上执行,起到懒加载作用。
  6. 类的静态属性只有在第一次使用的时候才会加载,JVM在类加载时是线程安全的
  7. */
  8. private static class SingletonInstance{
  9. private static final Singleton02 INSTANCE = new Singleton02();
  10. }
  11. public static Singleton02 getInstance(){
  12. return SingletonInstance.INSTANCE;
  13. }
  14. }

3.枚举

枚举方式是最简单的写法,也是被很多人推崇的写法

  1. enum Singleton03{
  2. INSTANCE;
  3. }

简单明了...

四、小结

使用单例模式,可以使一个类只存在一个实例对象,从而节省了系统资源。

上文中列出了8个写法,其中懒加载的写法存在线程安全和效率的问题,需要谨慎使用。比较推荐的写法有5种:懒加载2种+其他方式3种。当认定单例的对象在软件中一定会用到,可以使用懒加载,反之可以使用其他方式

原文链接:http://www.cnblogs.com/Weihaom/p/14234995.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号