单例模式(Singleton)

单例对象(Singleton)是一种经常使用的设计模式。

在Java应用中,单例对象能保证在一个JVM中,该对象仅仅有一个实例存在。单例模式也分三种:懒汉式单例、饿汉式单例、登记式单例。

单例模式有几个长处:

  • 1、某些类创建比較频繁,对于一些大型的对象,这是一笔非常大的系统开销。
  • 2、省去了new操作符,减少了系统内存的使用频率,减轻GC压力。
  • 3、有些类如交易所的核心交易引擎。控制着交易流程,假设该类能够创建多个的话,系统全然乱了。(比方一个军队出现了多个司令员同一时候指挥,肯定会乱成一团),所以仅仅有使用单例模式,才干保证核心交易server独立控制整个流程。

首先我们写一个简单的单例类(懒汉式):

  1. public class Singleton {
  2. /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟载入 */
  3. private static Singleton instance = null;
  4. /* 私有构造方法,防止被实例化 */
  5. private Singleton() {
  6. }
  7. /* 静态project方法。创建实例 */
  8. public static Singleton getInstance() {
  9. if (instance == null) {
  10. instance = new Singleton();
  11. }
  12. return instance;
  13. }
  14. /* 假设该对象被用于序列化。能够保证对象在序列化前后保持一致 */
  15. public Object readResolve() {
  16. return instance;
  17. }
  18. }

这个类能够满足基本要求,可是。像这样毫无线程安全保护的类。假设我们把它放入多线程的环境下,肯定就会出现故障了,怎样解决?我们首先会想到对getInstance方法加 synchronized keyword,例如以下:

  1. public static synchronized Singleton getInstance() {
  2. if (instance == null) {
  3. instance = new Singleton();
  4. }
  5. return instance;
  6. }

可是。synchronizedkeyword锁住的是这个对象,这种使用方法,在性能上会有所下降,由于每次调用getInstance(),都要对对象上锁。事实上,仅仅有在第一次创建对象的时候须要加锁,之后就不须要了,所以,这个地方须要改进。

我们改成以下这个:

  1. public static Singleton getInstance() {
  2. if (instance == null) {
  3. synchronized (instance) {
  4. if (instance == null) {
  5. instance = new Singleton();
  6. }
  7. }
  8. }
  9. return instance;
  10. }

似乎攻克了之前提到的问题,将synchronizedkeyword加在了内部。也就是说当调用的时候是不须要加锁的,仅仅有在instance为null,并创建对象的时候才须要加锁,性能有一定的提升。可是。这种情况,还是有可能有问题的,看以下的情况:在Java指令中创建对象和赋值操作是分开进行的。也就是说instance = new Singleton();语句是分两步执行的。

可是JVM并不保证这两个操作的先后顺序。也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了。我们以A、B两个线程为例:

a)A、B线程同一时候进入了第一个if推断

b)A首先进入synchronized块,由于instance为null,所以它执行 instance = new Singleton();

c)由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有開始初始化这个实例),然后A离开了synchronized块。

d)B进入synchronized块,由于instance此时不是null,因此它立即离开了synchronized块并将结果返回给调用该方法的程序。

e)此时B线程打算使用Singleton实例,却发现它没有被初始化,于是发生错误了。

所以程序还是有可能发生错误,事实上程序在执行过程是非常复杂的,从这点我们就能够看出,尤其是在写多线程环境下的程序更有难度。有挑战性。

我们对该程序做进一步优化(饿汉式):

  1. private static class SingletonFactory{
  2. private static Singleton instance = new Singleton();
  3. }
  4. public static Singleton getInstance(){
  5. return SingletonFactory.instance;
  6. }

实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被载入的时候。这个类的载入过程是线程相互排斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance仅仅被创建一次。并且会保证把赋值给instance的内存初始化完成。这样我们就不用操心上面的问题。

同一时候该方法也仅仅会在第一次调用的时候使用相互排斥机制,这样就攻克了低性能问题。

这样我们临时总结一个完美的单例模式:

  1. public class Singleton {
  2. /* 私有构造方法。防止被实例化 */
  3. private Singleton() {
  4. }
  5. /* 此处使用一个内部类来维护单例 */
  6. private static class SingletonFactory {
  7. private static Singleton instance = new Singleton();
  8. }
  9. /* 获取实例 */
  10. public static Singleton getInstance() {
  11. return SingletonFactory.instance;
  12. }
  13. /* 假设该对象被用于序列化,能够保证对象在序列化前后保持一致 */
  14. public Object readResolve() {
  15. return getInstance();
  16. }
  17. }

事实上说它完美也不一定,假设在构造函数中抛出异常,实例将永远得不到创建,也会出错。

所以说,十分完美的东西是没有的,我们仅仅能依据实际情况,选择最适合自己应用场景的实现方法。

也有人这样实现:由于我们仅仅须要在创建类的时候进行同步,所以仅仅要将创建和getInstance()分开。单独为创建加synchronizedkeyword,也是能够的:

  1. public class SingletonTest {
  2. private static SingletonTest instance = null;
  3. private SingletonTest() {
  4. }
  5. private static synchronized void syncInit() {
  6. if (instance == null) {
  7. instance = new SingletonTest();
  8. }
  9. }
  10. public static SingletonTest getInstance() {
  11. if (instance == null) {
  12. syncInit();
  13. }
  14. return instance;
  15. }
  16. }

考虑性能的话,整个程序仅仅需创建一次实例。所以性能也不会有什么影响。

补充:採用"影子实例"的办法为单例对象的属性同步更新

  1. public class SingletonTest {
  2. private static SingletonTest instance = null;
  3. private Vector properties = null;
  4. public Vector getProperties() {
  5. return properties;
  6. }
  7. private SingletonTest() {
  8. }
  9. private static synchronized void syncInit() {
  10. if (instance == null) {
  11. instance = new SingletonTest();
  12. }
  13. }
  14. public static SingletonTest getInstance() {
  15. if (instance == null) {
  16. syncInit();
  17. }
  18. return instance;
  19. }
  20. public void updateProperties() {
  21. SingletonTest shadow = new SingletonTest();
  22. properties = shadow.getProperties();
  23. }
  24. }

通过单例模式的学习告诉我们:

1、单例模式理解起来简单,可是详细实现起来还是有一定的难度。

2、synchronizedkeyword锁定的是对象,在用的时候,一定要在恰当的地方使用(注意须要使用锁的对象和过程,可能有的时候并非整个对象及整个过程都须要锁)。

到此,单例模式基本就介绍完了,事实上採用类的静态方法,实现单例模式的效果,也是可行的。此处二者有何不同呢?

  • 首先。静态类不能实现接口。

    (从类的角度说是能够的。可是那样就破坏了静态了。

    由于接口中不同意有static修饰的方法。所以即使实现了也是非静态的)

  • 其次。单例能够被延迟初始化,静态类一般在第一次载入是初始化。

    之所以延迟载入。是由于有些类比較庞大,所以延迟载入有助于提升性能。

  • 再次,单例类能够被继承。他的方法能够被覆写。

    可是静态类内部方法都是static。无法被覆写。

  • 最后,单例类比較灵活,毕竟从实现上仅仅是一个普通的Java类,仅仅要满足单例的基本需求。你能够在里面随心所欲的实现一些其他功能,可是静态类不行。

从上面这些概括中,基本能够看出二者的差别,可是,从还有一方面讲,上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有非常大的关联。仅仅是我们考虑问题的层面不同罢了。

两种思想的结合,才干造就出完美的解决方式,就像HashMap採用数组+链表来实现一样。事实上生活中非常多事情都是这样,单用不同的方法来处理问题,总是有长处也有缺点,最完美的方法是,结合各个方法的长处,才干最好的解决这个问题。

拓展:

什么是线程安全?假设你的代码所在的进程中有多个线程在同一时候执行,而这些线程可能会同一时候执行这段代码。假设每次执行结果和单线程执行的结果是一样的,并且其他的变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作。或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。那就是线程安全的。

java设计模式学习 ----- 单例模式(Singleton)的更多相关文章

  1. Java设计模式之单例模式 - Singleton

    用来创建独一无二的,是能有一个实例的对象的入场券.告诉你一个好消息,单例模式的类图可以说是所有模式的类图中最简单的,事实上,它的类图上只有一个类!但是,可不要兴奋过头,尽管从类设计的视角来说很简单,但 ...

  2. java设计模式之 单例模式 Singleton

    static 的应用 单例模式 Singleton 单例:保证一个类在系统中最多只创建一个实例. 好处:由于过多创建对象实例,会产生过多的系统垃圾,需要GC频繁回收,由于GC会占用较大的系统资源,所有 ...

  3. Java 设计模式(三)-单例模式(Singleton Pattern)

    1     概念定义 1.1   定义 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 1.2   类型 创建类模式 1.3   难点 1)多个虚拟机 当系统中的单例类被拷贝运行在多 ...

  4. java设计模式学习-单例模式

    java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供.”单例模式可以保证一个应用中有且只有一个实例,避免了资源的浪费和多个实例多次调用导致出错. 单例模式有以下特点: 1.单 ...

  5. Java 设计模式之单例模式(一)

    原文地址:Java 设计模式之单例模式(一) 博客地址:http://www.extlight.com 一.背景 没有太多原由,纯粹是记录和总结自己从业以来经历和学习的点点滴滴. 本篇内容为 Java ...

  6. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

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

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

  8. Java设计模式学习资源汇总

    本文记录了Java设计模式学习书籍.教程资源.此分享会持续更新: 1. 设计模式书籍 在豆瓣上搜索了一把,发现设计模式贯穿了人类生活的方方面面.还是回到Java与程序设计来吧. 打算先归类,再浏览,从 ...

  9. 设计模式(4) -- 单例模式(Singleton)

    设计模式(4)  -- 单例模式(Singleton) 试想一个读取配置文件的需求,创建完读取类后通过New一个类的实例来读取配置文件的内容,在系统运行期间,系统中会存在很多个该类的实例对象,也就是说 ...

随机推荐

  1. 路飞学城Python-Day3

    Moudle 1 Chapter 1 #练习题# 1.简述编译型与解释型语言的区别,且分别列出你知道的哪些语言属于编译型,哪些属于解释型?"""编译型:编译类指在应用源程 ...

  2. HDU-4296 Buildings 贪心 从相邻元素的相对位置开始考虑

    题目链接:https://cn.vjudge.net/problem/HDU-4296 题意 有很多板子,每一个板子有重量(w)和承重(s)能力 现规定一块板子的PDV值为其上所有板子的重量和减去这个 ...

  3. Django模版系统

    一.什么是模板? 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板. 二.模板语法分类 一.模板语法之变量:语法为 {{ }}: 在 Django 模板中遍历复杂数据结构的关键 ...

  4. 学习参考《父与子的编程之旅python【第二版】》高清中文版PDF+高清英文版PDF+源代码

    对于初步接触编程语言的朋友,推荐看一看<父与子的编程之旅第2版>,对于完全编程零基础的很友好! 图文并茂,过多的文字堆垒很容易让人产生厌倦情绪,也更容易让人产生放弃的想法.使用了大量插图, ...

  5. linux在线添加硬盘、扫盘

    1:查看scsi 硬盘设备[root@web02 ~]# ls /sys/class/scsi_host host0 host1 host2 2:检查本机现有硬盘 [root@web02 ~]# fd ...

  6. C - The C Answer (2nd Edition) - Exercise 1-12

    /* Write a program that prints its input one word per line. */ #include <stdio.h> #define IN 1 ...

  7. [Webpack + React] Import CSS Modules with TypeScript and webpack

    If you try to use CSS Modules in TypeScript the same way you would use them in JavaScript, with webp ...

  8. MySQL 以及 Python 实现排名窗体函数

    大部分数据库都提供了窗体函数.比方RANK,ROW_NUMBER等等. MySQL 这方面没有直接提供.可是能够变相的实现.我曾经写了row_number 的实现,今天有时间把 rank 的实现贴出来 ...

  9. Unity游戏开发--30s制作精美地图

    "君子生非异也.善假于物也"--<劝学>荀子 引用这句话的目的,是我觉得有时候.利用工具来提高游戏开发效率是很必要的. 利用工具,解放程序员双手. 今天想给大家介绍下. ...

  10. vim 插件之solarized

    solarized 其实算不上严格的插件,它只是一个主题,这个主题看起来还是很不错的.有一点,因为vim的最终效果还跟ubuntu终端配色有关,所以我们还需要进行其他的设置.具体过程如下 1.更改终端 ...