设计模式-单例

定义

什么是单例模式?
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

举个例子 ——Windows任务管理器
通常情况下,无论我们启动任务管理多少次,Windows系统始终只能弹出一个任务管理器窗口。
为什么要这样设计呢?我们可以从以下两个方面来分析:其一,如果能弹出多个窗口,且这些窗口的内容完全一致,全部是重复对象,这势必会浪费系统资源,任务管理器需要获取系统运行时的诸多信息,这些信息的获取需要消耗一定的系统资源,包括CPU资源及内存资源等,而且根本没有必要显示多个内容完全相同的窗口;其二,如果弹出的多个窗口内容不一致,问题就更加严重了,这意味着在某一瞬间系统资源使用情况和进程、服务等信息存在多个状态。

两种类型

单例模式的类型
单例模式有两种类型:
懒汉式:在真正需要使用对象时才去创建该单例类
饿汉式:在类加载时已经创建好该单例对象,等待被程序使用
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化,若已实例化直接返回该类对象,否则则先执行实例化操作。
在这里插入图片描述

饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
关于类加载,涉及到JVM的内容,我们目前可以简单认为在程序启动时,这个单例对象就已经创建好了。
在这里插入图片描述

类图

  • 懒汉式
    在这里插入图片描述
class LazySingleton 
{ 
    private static LazySingleton instance = null; 
 
    private LazySingleton() { } 
 
    synchronized public static LazySingleton getInstance() 
    { 
        if (instance == null) 
        {
            instance = new LazySingleton(); 
        }
        return instance; 
    }
}
public static LazySingleton getInstance() 
{ 
    if (instance == null) 
    {
        synchronized (LazySingleton.class) 
        {
            instance = new LazySingleton(); 
        }
    }
    return instance; 
}

该懒汉式单例类在getInstance()方法前面增加了关键字synchronized进行线程锁,以处理多个线程同时访问的问题。但是,上述代码虽然解决了线程安全问题,但是每次调用getInstance()时都需要进行线程锁定判断,在多线程高并发访问环境中,将会导致系统性能大大降低。
由于实现了synchronized加锁机制,线程A进入synchronized锁定的代码中执行实例创建代码,线程B处于排队等待状态,必须等待线程A执行完毕后才可以进入synchronized锁定代码。但当A执行完毕时,线程B并不知道实例已经创建,将继续创建新的实例,导致产生多个单例对象,违背单例模式的设计思想,因此需要进行进一步改进,在synchronized中再进行一次(instance == null)判断,这种方式称为双重检查锁定(Double-Check Locking)。使用双重检查锁定实现的懒汉式单例类完整代码如下所示:

class LazySingleton 
{ 
    private volatile static LazySingleton instance = null; 
 
    private LazySingleton() { } 
 
    public static LazySingleton getInstance() 
    { 
        //第一重判断
        if (instance == null) 
        {
            //锁定代码块
            synchronized (LazySingleton.class) 
            {
                //第二重判断
                if (instance == null) 
                {
                    instance = new LazySingleton(); //创建单例实例
                }
            }
        }
        return instance; 
    }
}
  • 饿汉式
    在这里插入图片描述从上图中可以看出,由于在定义静态变量的时候实例化单例类,因此在类加载的时候就已经创建了单例对象,代码如下所示:
class EagerSingleton 
{ 
    private static final EagerSingleton instance = new EagerSingleton(); 
    private EagerSingleton() { } 
 
    public static EagerSingleton getInstance() 
    {
        return instance; 
    }   
}

实现举例

再以上面任务管理器为例:

class TaskManager
{
     private static TaskManager tm = null;
     public TaskManager() {……}                         //初始化窗口
     public void displayProcesses() {……}               //显示进程
     public void  displayServices() {……}               //显示服务
     public static TaskManager getInstance()
     {
        if (tm == null)
        {
            tm = new TaskManager();
        }
        return tm;
     }
}
class TaskManager
{
     private static TaskManager tm = new TaskManager();
     public TaskManager() {……}                         //初始化窗口
     public void displayProcesses() {……}               //显示进程
     public void  displayServices() {……}               //显示服务
     public static TaskManager getInstance()
     {
        return tm;
     }
}

小结

饿汉式单例类在类被加载时就将自己实例化,它的优点在于无须考虑多线程访问问题,可以确保实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一开始就得以创建,因此要优于懒汉式单例。
但是无论系统在运行时是否需要使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时由于需要创建饿汉式单例对象,加载时间可能会比较长。

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值