【大话设计模式】模式一 :单例模式与多线程

一、基本介绍

单例模式(Singleton),保证一个类仅仅有一个实例,并且提供一个访问它的全局访问点。

        通常我们可以让一个全局变量使其被一个对象访问,但是它不能防止实例化多个对象。最好的方法就是,让类自身负责保存它的唯一实例,同时这个类保证没有其他实例可以被创建,并且它提供了一个访问该实例的方法。​​​

模式构成要素:

  • 私有构造方法
  • 私有静态引用指向自己的实例
  • 以自己实例为返回值的公有静态方法

二、模式应用场景及举例

1、适用场景:
1.需要生成唯一序列的环境。
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
4.方便资源相互通信的环境。

2、场景举例

以下是单例模式的经典使用场景: 

1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。 

其他应用场景举例: 
    1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 
    2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 
    3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 
    4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 
    5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 
    6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 
    7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。 
    8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 
    9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 
    10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例. 

三、实现方式以及优缺点

 1.饿汉式:单例实例在类装载时就构建,急切初始化。(预先加载法) 

public class Singleton {
        private Singleton() {
        }
        public static Singleton instance = new Singleton();
        public Singleton getInstance() {
                return instance;
        }
}

优点 :

  • 它基于 classloder 机制避免了多线程的同步问题,线程安全 
  • 在类加载的同时已经创建好一个静态对象,调用时反应速度快 

缺点: 

  • 类加载时就初始化,浪费内存。
  • 资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化

 另外一种实现,静态内部类

public class Singleton {
        private Singleton() {
        }

        private static class SingletonBuilder {
            static Singleton instance = new Singleton();
        }

        public static Singleton getInstance() {
            return SingletonBuilder.instance;
        }
    }
/*
    优点:
    资源利用率高,不执行getInstance()不被实例,可以执行该类其他静态方法
    缺点:
    第一次加载时反应不够快*/

2.懒汉式:单例实例在第一次被使用时构建,延迟初始化。

public class Singleton {
    private static Singleton instance;
    //构造器私有化
    private Singleton() {
    }
    /**
     * 若实例存在返回实例,否则创建实例
     * @return instance实例
     */
    public static Singleton getInstance(){

        if(instance ==null){
            instance =new Singleton();
        }
        return instance;
    }
}

优点: 

        避免了饿汉式的那种在没有用到的情况下创建事例,资源利用率高,不执行getInstance()就不会被实例,可以执行该类的其他静态方法。 

缺点: 

         懒汉式在单个线程中没有问题,但多个线程同事访问的时候就可能同事创建多个实例,而且这多个实例不是同一个对象,虽然后面创建的实例会覆盖先创建的实例,但是还是会存在拿到不同对象的情况。解决这个问题的办法就是加锁synchonized,第一次加载时不够快,多线程使用不必要的同步开销大。 


3、多线程时的单例模式

public class Singleton {
    private static Singleton instance;
    //构造器私有化
    private Singleton() {
    }
    public static Singleton getInstance() {
        //临界区互斥访问
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        return instance;
    }
}

        这样有个很大的问题,每次调用GetInstance方法都需要lock,很大地影响性能,因此需要改良,所以有了双重锁定(Double-Check-Lock)。

4、双重锁定(Double-Check-Lock)

public class Singleton {
    private static Singleton instance;
    //构造器私有化
    private Singleton() {
    }
    public static Singleton getInstance() {
        //临界区互斥访问
        //先判断实例是否存在,不存在再进行加锁处理
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    //检查该单例是否已经被创建,保证单例模式
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点 :资源利用率高,不执行getInstance()就不被实例,可以执行该类其他静态方法 。
缺点 :第一次加载时反应不快,由于java内存模型一些原因偶尔失败 。

四、JDK中单例模式

1、java.awt.Toolkit

        ToolKit 是一个抽象类,ToolKit 作为 AWT 工具箱,提供了 GUI 最底层的 Java 访问,例如从系统获取图像、获取屏幕分辨率,获取屏幕色彩模型、全屏的时候获得屏幕大小等。很多时候并不常用,适合使用懒汉模式,使用饿汉反而会导致虚拟机JVM启动过慢。

 2、Runtime类🔍是使用的单例设计模式。

        采用饿汉模式,提高虚拟机加载速度。

 五、总结

总结: 
    一般采用饿汉式,若对资源十分在意可以采用静态内部类,不建议采用懒汉式及双重检测

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
单例模式是一种设计模式,它保证类在整个程序中只能创建一个实例。在多线程环境下,如果不加以处理,可能会出现多个线程同时调用getInstance()方法创建实例的问题,从而违反了单例模式的原则。 为了在多线程环境下保证单例模式的正确性,可以采用以下几种解决方案: 1. 懒汉式-线程不安全:在 getInstance() 方法中进行实例化时,没有进行多线程并发控制,可能会导致创建多个实例的问题。 2. 懒汉式-线程安全:在 getInstance() 方法加上 synchronized 关键字,使用同步锁来控制多线程并发访问,确保只有一个线程能够创建实例。但是,由于加锁会造成多线程竞争锁资源的性能损耗,因此并不推荐使用该方式。 3. 饿汉式:在类加载时就进行实例化,保证了线程安全,不存在并发问题。但是,由于直接创建对象实例,可能会占用空间,影响程序的性能。 4. 双重检查锁定:使用 volatile 关键字来保证多线程环境下的可见性,通过两次判断实例是否为 null 来控制并发访问。第一次判断是为了避免不必要的同步锁开销,第二次判断是为了在实例为 null 的情况下进行同步锁。这种方式可以避免懒汉式加锁方式的性能问题。 5. 静态内部类:利用类加载机制和类初始化锁的特性,在静态内部类中创建实例,保证了线程安全性和延迟加载。通过静态内部类的方式创建单例,只有在调用 getInstance() 方法时才会加载内部类,从而实现了懒加载。 综上所述,针对多线程环境下的单例模式,可以根据具体需求选择适当的实现方式。在保证线程安全的前提下,尽量避免加锁操作,以提高程序的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枫蜜柚子茶

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值