单例模式

栏目: 后端 · 发布时间: 5年前

内容简介:单例模式为GOF设计模式中的一种,当我们创建的对象需要昂贵的资源,且该对象又被频繁使用,同时,这个对象的状态是共享的,为了避免资源的浪费以及加载资源的时间,我们往往会引入单例模式。因此,要实现单例模式,其核心就是要在Java语言中,通常提供了如下几种单例模式的实现方式。通过提供一个静态的对象

Preface

单例模式为GOF设计模式中的一种,当我们创建的对象需要昂贵的资源,且该对象又被频繁使用,同时,这个对象的状态是共享的,为了避免资源的浪费以及加载资源的时间,我们往往会引入单例模式。因此,要实现单例模式,其核心就是要 在已有该类的实例前提下,阻止其他开发人员再创建类的新实例

Java 语言中,通常提供了如下几种单例模式的实现方式。

懒汉模式

public class SingletonDemo {
    private static SingletonDemo instance;

    private SingletonDemo() {
    }

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

通过提供一个静态的对象 instance ,利用 private 权限的构造方法和 getInstance() 方法来给予访问者一个单例。缺点是,没有考虑到线程安全,可能存在多个访问者同时访问,并同时构造了多个对象的问题。之所以叫做懒汉模式,主要是因为此种方法可以非常明显的 lazy loading 。针对懒汉模式 线程不安全 的问题,我们自然想到了,在 getInstance() 方法前加锁,于是就有了第二种实现。

线程安全的懒汉模式

public class SingletonDemo {
    private static SingletonDemo instance;

    private SingletonDemo() {
    }

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

然而并发其实是一种特殊情况,大多时候这个锁占用的额外资源都浪费了,这种打补丁方式写出来的结构效率很低。

饿汉模式

public class SingletonDemo {
    private static SingletonDemo instance = new SingletonDemo();

    private SingletonDemo() {
        System.out.println("Singleton has loaded");
    }

    public static SingletonDemo getInstance() {
        return instance;
    }
    
    public static void printHello() {
        System.out.println("hello");
    }
}

由于 instance 成员变量使用 static 修饰,在 JVM 首次加载该类时单例对象 instance 会被创建并 常驻内存 ,所以是线程安全的。但显然,这种方法没有起到 lazy loading 的效果。举个例子,也许你只想调用 SingletonDemo.printHello() 方法,但同时打印了 ”Singleton has loaded“,这可能是你不想见到的。

所以我们引入静态类内部加载的方式,也算是单例模式较为完善的解决方案。

静态类内部加载

public class SingletonDemo {
    private SingletonDemo() {
    }

    public static SingletonDemo getInstance() {
        return SingletonHolder.instance;
    }

    private static class SingletonHolder {
        private static SingletonDemo instance = new SingletonDemo();
    }
}

使用 私有内部类 的好处是,静态内部类不会在单例加载时就加载,而是在调用 getInstance() 方法时才进行加载。且单例的创建是在 SingletonHolder 被 JVM 加载时完成,故而也是线程安全的。因此该方法兼备了以上两种实现的优点。

双重校验锁法

public class SingletonDemo {
    private volatile static SingletonDemo instance;

    private SingletonDemo() {
        System.out.println("Singletonhasloaded");
    }

    public static SingletonDemo getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }
}

接下来我解释一下在并发时,双重校验锁法会有怎样的情景:

getInstance()
getInstance()

理论上双重校验锁法是线程安全的,并且,这种方法实现了lazy loading。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

创业者手册

创业者手册

[美] 史蒂夫·布兰克、[美] 鲍勃·多夫 / 新华都商学院 / 机械工业出版社 / 2013-1 / 89.00元

我们发现,企业的成功程度和创始人使用本书的频繁程度成正比。书中折角越多,书被翻得越破,企业取得的成功就越显著。阅读本书切忌囫囵吞枣。 所有创业者都坚信自己的道路与众不同,他们在踏上创业之路时从不设计路线图,认为其他模式或模板并不适合自己。同样是初创企业,有些能够取得成功而有些只能沦落到廉价清库的下场,看起来这似乎是运气使然,然而事实并非如此。英雄成功的故事都是一样的。初创企业实现成功之路肯定......一起来看看 《创业者手册》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换