不管你年底换不换工作,了解下单例模式

1. 单例模式

什么是单例模式?简言之就是确保定义为单例模式的类在程序中有且只有一个实例。单例模式的特点:

  1. 只有一个实例 (只能有一个对象被创建)
  2. 自我实例化(类构造器私有)
  3. 对外提供获取实例的静态方法

2.单例模式的实现

常见的单例模式实现方式有五种:

2.1. 懒汉式

懒汉式(一般也称之为 饱汉式),具体代码实现如下:


public class Singleton {

    /**
     * 自我实例化
     */
    private static Singleton singleton;

    /**
     * 构造方法私有
     */
    private Singleton() {
        System.out.println("创建单例实例...");
    }

    /**
     * 对外提供获取实例的静态方法
     */
    public static Singleton getInstance() {
        if (null == singleton) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

从代码实现中可以看到,实例并不是在一开始就是初始化的,而是在调用 getInstance()方法后才会产生单例,这种模式延迟初始化实例,但它并非是线程安全的。

public class SingleTonTest {

    /**
     * 多线程模式下测试懒汉模式是否线程安全
     *
     * @param args
     */
    public static void main(String[] args) {
        /**
         * 这里我图方便,直接用Executors创建线程池
         * 阿里巴巴开发手册是不推荐这么做的
         */
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 20; i++) {
            executorService.execute(() -> System.out.println(Thread.currentThread().getName() + "::" + Singleton.getInstance()));
        }
    }
}

测试结果截图:

懒汉式是在运行时加载对象的,所以加载该单例类时会比较快,但是获取对象会比较慢。且这样做是线程不安全的,如果想要线程安全,可以在getInstance()方法加上synchronized 关键词修饰,但这样会让我们付出惨重的效率代价。

2.2. 饿汉式

提前创建好实例对象,调用效率高,但无法延时加载,容易产生垃圾,线程安全。

public class Singleton {

    /**
     * 自我实例化
     */
    private static Singleton singleton = new Singleton();

    /**
     * 构造方法私有
     */
    private Singleton() {
        System.out.println("创建单例实例...");
    }

    /**
     * 对外提供获取实例的静态方法
     */
    public static Singleton getInstance() {
        return singleton;
    }
}

2.3. 双重检查锁模式

public class Singleton {

    /**
     * 自我实例化,volatile修饰,保证线程间可见
     */
    private volatile static Singleton singleton;

    /**
     * 构造方法私有
     */
    private Singleton() {
        System.out.println("创建单例实例...");
    }

    /**
     * 对外提供获取实例的静态方法
     */
    public static Singleton getInstance() {
        // 第一次检查,避免不必要的实例
        if (singleton == null) {
            // 第二次检查,同步,避免产生多线程的问题
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

由于singleton=new Singleton()对象的创建在JVM中可能会进行重排序,在多线程访问下存在风险,使用volatile修饰signleton实例变量,能禁止指令重排,使得对象在多线程间可见,能够有效解决该问题。

双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是这些习语失败的一个主要原因

2.4. 静态内部类模式

public class Singleton {
    /**
     * 构造方法私有
     */
    private Singleton() {
        System.out.println("创建单例实例...");
    }

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

    private static Singleton getInstance() {
        return SingletonInner.singleton;
    }
}

这样写充分利用静态内部类的特点——初始化操作和外部类是分开的,只有首次调用getInstance()方法时,虚拟机才加载内部类(SingletonInner.class)并初始化instance, 保证对象的唯一性。

2.5. 枚举单例模式

public enum Singleton {
    INSTANCE
}

感觉异常简单,默认枚举类创建的对象都是单例的,且支持多线程。

3.单例模式总结

  1. 单例模式优点在于:全局只会生成单个实例,所以能够节省系统资源,减少性能开销。然而也正是因为只有单个实例,导致该单例类职责过重,违背了“单一职责原则”,单例类也没有抽象方法,会导致比较难以扩展。
  2. 以上所有单例模式中,推荐使用静态内部类的实现,非常直观,且保证线程安全。在《Effective Java》中推荐枚举类,但太简单了,导致代码的可读性比较差。
  3. 单例模式是创建型模式,反序列化时需要重写readResovle()方法,以保证实例唯一。

文章首发于本人博客:www.developlee.top, 转载请注明出处!

关注公众号,后台回复666, 领取福利:

原文地址:https://www.cnblogs.com/liululee/p/11968258.html

时间: 2024-11-23 16:22:39

不管你年底换不换工作,了解下单例模式的相关文章

合并当前工作簿下的所有工作表

Sub 合并当前工作簿下的所有工作表() Dim j As Single Application.ScreenUpdating = False For j = 1 To Sheets.Count If Sheets(j).Name <> ActiveSheet.Name Then X = Range("A65536").End(xlUp).Row + 1 Sheets(j).UsedRange.Copy Cells(X, 1) End If Next Range("

一加坑爹无售后,强迫客户换新机,线下商户不承认一加

写此帖,不知道会不会被一加删帖!!! 但是我句句实情!真心遭遇! 我被一加手机坑了! 2014/05/28 第一批一加官网开始抢购,冲着4G.宣传如此火爆,我以¥1,999.99的价格抢购到了一加!兴奋! 2014/09/03 正在走路时,手中持一加手机,不小心掉地上了,本人 165CM,手垂直拿手机,估计手机离地面 30CM?表面屏幕 右上脚摔碎了,约 2平方CM 一加手机,我很生气: 1.手机系统啥都正常,奈何键盘无反应? 2.才2个多月就那么低的底面就摔碎了? 3.就碎右上角2平方CM,触

合并一个工作簿下的多个工作表

工作中经常会碰到需要合并多个sheet,下面用VBA来解决此问题,代码如下: Sub 合并当前工作簿下的所有工作表() Application.ScreenUpdating = False For j = 1 To Sheets.Count    If Sheets(j).Name <> ActiveSheet.Name Then        X = Range("A1048576").End(xlUp).Row + 1        Sheets(j).UsedRang

iOS下单例模式实现(objective-c arc gcd)

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 这里主要介绍下在arc下,利用gcd实现单例. 第一步:声明一个静态实例 static SoundTool *_instance; 第二步:重写初始化方法 + (id)allocWithZone:(struct _NSZone *)zone 在对象初始化分配内存的时候都会调用这个方法,重写该方法时,即便用户没用通过shared方法获取实例,自己初始化依然可以保证得到的是同一个实例. 在gcd后,多线程下保证一个代码只被执

在北上广工作压强下,幸福是怎么炼成的

前几日,一位学弟,要出来实习,找到我,问我有没有好的介绍.按照惯例,我就推荐他去了电视台,新闻学子嘛,电视台肯定能提供最好的锻炼. 然而,才过了几天,电视台老师就打电话告诉我,那孩子不干了走了,来了也不愿意干活.紧接着,那学弟也打电话给我抱怨,说那里都是找他做苦力,还没什么钱.我愕然,仔细聊过才知道,现在孩子们找实习也都要高薪,还希望工作不累. 突然想起来,前几天,一个朋友听说IT行业收入超高,毅然决绝辞了编辑工作,依仗有点编程基础,去报名学习测试开发去了,学费据说就交了三四万,突然发现,时代真

Linux中文本编辑器三种工作模式切换及vi编辑器三种工作模式下命令详解

文本编辑器的作用 创建或修改文本文件维护Linux系统中的各种配置文件Linux中最常用的文本编辑器Linux中最常用的文本编辑器vi:类UNIX操作系统的默认文本编辑器vim:vim时vi文本编辑器(一般简称vi编辑器)的增强版本 vi编辑器的工作模式 三种工作模式命令模式.输入模式.末行模式不同模式之间的切换 命令模式的基本操作3-1 光标移动方向键移动(↑.↓.←.→) 功能:上下左右移动Page Down或Ctrl+F 功能:向下翻动一整页的内容Page Up或Ctrl+B 功能:向上翻

5个月后换PHP工作

在这5个月中的学习内容: 刚开始学是不容易,忘了正常,要注意总结. (1)html+css+js (2)PHP基础 (3)smarty+thinkphp php基础最重要 smarty和thinkphp 一个一星期就学会了. smarty是模板引擎.不是语言,只不过是用PHP开发的. thinkphp是框架. 学完PHP,不管他织梦,还是织布.统统都迎刃而解. 业余,每天保证两个小时有效学习时间.5个月应该没问题. PHP学习视频: 3w.lampbrother.net/php/html/PHP

多线程下单例模式:懒加载(延迟加载)和即时加载

在开发中,如果某个实例的创建需要消耗很多系统资源,那么我们通常会使用惰性加载机制,也就是说只有当使用到这个实例的时候才会创建这个实例,这个好处在单例模式中得到了广泛应用.这个机制在single-threaded环境下的实现非常简单,然而在multi-threaded环境下却存在隐患.本文重点介绍惰性加载机制以及其在多线程环境下的使用方法.(作者numberzero,参考IBM文章<Double-checked locking and the Singleton pattern>,欢迎转载与讨论

Swift下单例模式

import UIKit   class DataCenter: NSObject {       class let dataCenterObj:DataCenter = DataCenter()       class func getDataCenter() ->DataCenter {         return dataCenterObj     } }