博文原址:折腾Java设计模式之单例模式

单例模式

Ensure a class has only one instance, and provide a global point of access to it.

一个类仅仅只有一个实例,并且提供全局的接入点。简洁点理解就是涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问它自己唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

饿汉式单例模式

public final class EagerSingleton {

    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {
} public static EagerSingleton getInstance() {
return INSTANCE;
}
}

基于 classloader 机制避免了多线程的同步问题,不过INSTANCE在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法。类在加载时就初始化了,会浪费空间,因为不管你用还是不用,它都创建出来了,但是因为没有加锁,执行效率较高。

懒汉式单例模式

/**
* 懒汉式的单例模式-线程不安全的
*/
public final class LazyThreadNotSafeSingleton { private static LazyThreadNotSafeSingleton INSTANCE; private LazyThreadNotSafeSingleton() {
} public static LazyThreadNotSafeSingleton getInstance() {
if (null == INSTANCE) {
INSTANCE = new LazyThreadNotSafeSingleton();
}
return INSTANCE;
}
}
/**
* 懒汉式的单例模式-线程安全的
*/
public final class LazyThreadSafeSingleton { private static LazyThreadSafeSingleton INSTANCE; private LazyThreadSafeSingleton() {
} public static synchronized LazyThreadSafeSingleton getInstance() {
if (null == INSTANCE) {
INSTANCE = new LazyThreadSafeSingleton();
}
return INSTANCE;
}
}

有上述两种懒汉式单例模式,区别在与静态工厂方法getInstance是否加了synchronized修饰来进行同步,用于支持线程安全。懒汉式,在其加载对象的时候是不会创建对象实例的,只有等它真正使用的时候才会创建,如果一直没有使用则一直不会创建,能够避免内存浪费,也就是只有第一次调用的时候才会创建。但是加锁synchronized就影响了性能和效率,导致getInstance方法的性能受影响,此种方式也不推荐。寻找一种既能线程安全又可以延迟加载的方式。

双检查锁的单例模式

/**
* 双检查锁的单例模式-线程安全
*/
public final class DoubleCheckLockingSingleton { private static volatile DoubleCheckLockingSingleton INSTANCE; private DoubleCheckLockingSingleton() {
} public static DoubleCheckLockingSingleton getInstance() {
// 第一次检查实例是否存在,如果存在即可返回,不存在则进入同步块
if (null == INSTANCE) {
// 同步块,线程安全
synchronized (DoubleCheckLockingSingleton.class) {
// 第二次检查实例是否存在,如果还不存在则会真正的创建实例
if (null == INSTANCE) {
INSTANCE = new DoubleCheckLockingSingleton();
}
}
}
return INSTANCE;
}
}

双检查加锁的方式,能实现线程安全,又能减少性能的影响。双检查加锁,旨在每次调用getInstance方法都需要同步,但是先不会同步,在第一次判断实例是否存在后,如果不存在才进入同步块,进入同步块后,第二次检查实例是否存在,如果不存在,在同步块内创建实例。如此只有首次才会同步,从而减少了多次在同步情况下进行判断所浪费的时间。双检查加锁机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。但是实现过程稍微复杂点。

静态内部类Holder式单例模式

/**
* 静态内部类Holder式单例
*
* 延迟加载和线程安全
*/
public final class LazyInitializationHolderSingleton { private LazyInitializationHolderSingleton() {
} public static LazyInitializationHolderSingleton getInstance() {
return InstanceHolder.INSTANCE;
} /**
* 延迟加载
*/
private static class InstanceHolder { private static final LazyInitializationHolderSingleton INSTANCE = new LazyInitializationHolderSingleton();
}
}

getInstance方法第一次被调用的时候,它第一次读取InstanceHolder.INSTANCE,导致InstanceHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。其中使用到类的静态内部类和多线程缺省同步锁。

静态内部类

静态内部类指,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。静态内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。静态内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。

多线程缺省同步锁

在多线程开发中,为解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:

1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时;

2.访问final字段时;

3.在创建线程之前创建对象时;

4.线程可以看见它将要处理的对象时

枚举类型的单例模式

/**
* 采用枚举类型的单例模式
*/
public enum SingletonEnum { INSTANCE; @Override
public String toString() {
return getDeclaringClass().getCanonicalName() + "@" + hashCode();
} public void something(){
//do something...
} }

简洁,自动支持序列化机制,绝对防止多次实例化。《高效Java 第二版》中的说法:单元素的枚举类型已经成为实现Singleton的最佳方法。用枚举来实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。

总结

不建议使用懒汉式,简单的阔以使用饿汉式。涉及到反序列化创建对象时阔以使用枚举方式。如果考虑到延迟加载 的话,阔以采用静态内部类Holder的模式。如果对业务需求有特殊要求的时候阔以采用双检查锁的单例。

参考

源码地址

单例模式|菜鸟教程

《JAVA与模式》之单例模式

欢迎关注了解最新动态更新

折腾Java设计模式之单例模式的更多相关文章

  1. java 设计模式之单例模式

    -------Success is getting what you want, happiness is wanting what you get. java设计模式之单例模式(Singleton) ...

  2. 折腾Java设计模式之建造者模式

    博文原址:折腾Java设计模式之建造者模式 建造者模式 Separate the construction of a complex object from its representation, a ...

  3. 折腾Java设计模式之中介者模式

    博文原址:折腾Java设计模式之中介者模式 中介者模式 中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性.这种模式提供了一个中介类,该类通常处理不同类之间的通信,并 ...

  4. 折腾Java设计模式之备忘录模式

    原文地址:折腾Java设计模式之备忘录模式 备忘录模式 Without violating encapsulation, capture and externalize an object's int ...

  5. 折腾Java设计模式之状态模式

    原文地址 折腾Java设计模式之状态模式 状态模式 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式.在状态模式中,我们创建表示各种状态的对象 ...

  6. 折腾Java设计模式之模板方法模式

    博客原文地址:折腾Java设计模式之模板方法模式 模板方法模式 Define the skeleton of an algorithm in an operation, deferring some ...

  7. 折腾Java设计模式之访问者模式

    博客原文地址:折腾Java设计模式之访问者模式 访问者模式 Represent an operation to be performed on the elements of an object st ...

  8. 折腾Java设计模式之命令模式

    博客原文地址 折腾Java设计模式之命令模式 命令模式 wiki上的描述 Encapsulate a request as an object, thereby allowing for the pa ...

  9. Java设计模式之单例模式(七种写法)

    Java设计模式之单例模式(七种写法) 第一种,懒汉式,lazy初始化,线程不安全,多线程中无法工作: public class Singleton { private static Singleto ...

随机推荐

  1. redis与CPU、内存

    任何一个后端应用,包括代码都要考虑对于CPU和内存的影响.redis本质上类似于nodejs,单进程.单线程,事件驱动,但不同的是redis是CPU密集型的.这里列出了redis与内存CPU的相关考虑 ...

  2. appium-desktop录制脚本二次开发,生成我司自动化脚本

    目的 通过对appium-desktop脚本录制功能进行二次开发,使录制的java脚本符合我司自动化框架要求. 实现步骤 1.增加元素名称的输入框 由于ATK(我司自动化测试框架)脚本中元素是以“ap ...

  3. jdk源码阅读笔记-LinkedList

    一.LinkedList概述 LinkedList的底层数据结构为双向链表结构,与ArrayList相同的是LinkedList也可以存储相同或null的元素.相对于ArrayList来说,Linke ...

  4. 《前端之路》之 前端图片 类型 & 优化 & 预加载 & 懒加载 & 骨架屏

    目录 09: 前端图片 类型 & 优化 & 预加载 & 懒加载 & 骨架屏 09: 前端图片 类型 & 优化 & 预加载 & 懒加载 & ...

  5. 由dubbo服务禁用system.gc而引起的思考

    我一直都有一个疑问,丰巢业务服务的生产环境jvm参数设置是禁止system.gc的,也就是开启设置:-XX:+DisableExplicitGC,但是生产环境却从来没有出现过堆外内存溢出的情况.说明一 ...

  6. JavaScript构造函数

    JavaScript不同于其他强类型语言,没有类的概念,但是它支持可以与实例共同使用特殊的Constructor构造器,使用new关键字创建新的实例,并告知JavaScript使用对象的内规则去定制这 ...

  7. C# 填充Excel图表、图例背景色

    填充背景色,一般可以选择多种不同样式来填充背景,包括填充为纯色背景.渐变背景.图片背景或者纹理背景等.下面的内容将分别介绍通过C#来设置Excel中图表背景色.以及图表中的图例背景色的方法. 使用工具 ...

  8. 简单地做一下“回到顶部”按钮,用jQuery实现其隐藏和显示

    1.首先,我们要准备HTML代码: <div id="return-top"> <a href="#top">返回顶部</a> ...

  9. cmd 配置dchp服务器

    1.安装DHCP服务器角色,这样在netsh下才会有dhcp上下文 2.编写配置dhcp的脚本 从命令行运行netsh有两种语法: 比如要获取已经配置的网络接口列表 1.写全 netsh -r Rem ...

  10. MySQL和SQLyog的配置-安装及遇到的问题

    1. 安装MySQL 我先是参考了菜鸟教程上的#Windows 上安装 MySQL #版块的安装教程,不过经历了种种磨难,我最后是按照#此篇博客#安装成功的. 这两篇教程最大的差别是在于下载的版本不同 ...