关闭

Java 设计模式 之 单例模式

标签: java设计模式实例class
1655人阅读 评论(0) 收藏 举报
分类:

介绍 :

  • 单例模式是一种在日常开发中比较常见的设计模式,在这个模式当中只能允许被实例化一次。
  • 单例模式有两种构建方式,懒汉式与饿汉式,这两种构建方式会在后面详解
  • 单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。

对于那些还没有明白单例模式的朋友,我们可以用下面这张图来理解到底什么是单例模式

这里写图片描述

如果你还是不懂不要着急,我们用言语来形容一下单例模式


假如你现在正在工作,组长让你负责写一个关于对数据库进行操作的类(比如增删改查这些方法)学过jdbc的都知道,在这里我们暂且讲将这个对数据库操作的类称为 DateBaseTools, 如果我们要对数据库进行操作必须要连接数据库 通过一个Connection 的对象来对数据库进行操作,假如说现在有三个类需要对数据库进行操作,按照以往的惯例我们会在这三个类要对数据库进行操作的时候进行

DateBaseTools dbTools = new DateBaseTools();

上面的代码是我们最通常的做法,伟人说的好 量变引起质变,我们这个三个类对他进行操作就new了三个DateBaseTools对象,要是有几千次这种操作的话,就相当于new了几千次这个对象,虽然说在jvm中不用担心这种,jvm会对不用的对象进行回收,但是当jvm执行GC后,就会拖慢系统的运行速度,这并不是我们希望看到的,还有就是几千次实例化,就相当于调用了DateBasesTools的构造方法并且在构造方法中进行连接数据库又是比较费时的操作,这样一来还是比较耗时的。

这个时候单例模式就能解决这种问题,下面请看代码

public class DateBaseTools{
    private static DateBaseTools instance = null;
    private DateBaseTools {
        //数据库连接操作
    }
    public static DateBaseTools getSingInstance() {
        if (instance == null) {
            instance = new DateBaseTools();
        }
        return  instance;
    }
    public void action() {
    System.out.println(">>>>>>>>>>");
    }
}

可以看出我们在单例类中将自己实例化通过getSingInstance来获取到自己实例化的对象,只需要在第一次构造函数的时候将Connection保留就能在每次调用单例的时候对数据库进行操作了,以后只要这个对象不为空就不会实例化,如果我们要调用他里面的方法的话只需要

DateBaseTools.getSingInstance().action();

这样不管是调用了他多少次,他都只实例化了一次,调用了构造方法一次,当我们想起单例模式的时候。很多人的第一反应是写出如下的代码,包括教科书上也是这样教我们的。这也就是我们刚才所说的懒汉式

懒汉式 :

正如下面的代码

public class DateBaseTools{
    private static DateBaseTools instance = null;
    private DateBaseTools {
        //数据库连接操作
    }
    public static DateBaseTools getSingInstance() {
        if (instance == null) {
            instance = new DateBaseTools();
        }
        return  instance;
    }
    public void action() {
    System.out.println(">>>>>>>>>>");
    }
}

现在我们来说一下这种构建方式的优缺点

优点与缺点 :

  • 优点: 代码简单明了
  • 缺点: 线程不安全
    很多人看到优点觉得代码那么短,确实很简单,很好理解,但是看到缺点就不是很明白了,现在我们来说明下这个到底是怎么个不安全

很多时候我们为了高效会用到多线程,但是当有多个线程并行调用 getSingInstance() 的时候,就会创建多个实例。可能你还是不明白,为什么会创建多个实例呢,我们现在来模拟一下

第一个线程访问getSingInstance()的时候 发现引用的DateBaseTools为空,这样就会自己实例化一个DateBaseTools,但是在我们正在实例化的时候,或者是还没有实例化的时候,这个时候要是第二个线程 或者是其他线程来访问getSingInstace() ,这个时候的引用DateBaseTools还是为空 这样就会实例化,但是当我们其他的线程这个时候已经判断了为null,正在实例化,所以这样就违背了,我们用单例模式的初衷了


相信你只要仔细看了我上面的叙述,应该能够懂了为什么单例模式在多线程下不能正常工作了。
现在就让我们就可以在上面的代码稍微改动一下就能修复这个缺点
相信很多学过的线程的都会知道synchronized 所以我们只需要加上这个就能让我们的单例在多线程并发下运行了这也就是所谓的双重检验锁了

双重检验锁代码 :

public class DateBaseTools{
    private static DateBaseTools instance = null;
    private DateBaseTools {
        //数据库连接操作
    }
    public static DateBaseTools getSingInstance() {
        if (instance == null) {
            synchronized (DateBaseTools.class) {
                instance = new DateBaseTools();
            }
        }
        return  instance;
    }
    public void action() {
    System.out.println(">>>>>>>>>>");
    }
}

双重检验锁是一种使用同步块加锁的方法。程序员称其为双重检查锁,因为会有两次检查 instance == null,一次是在同步块外,一次是在同步块内。为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例了。


这段代码看上去已经很完美了,也解决了多线程的问题,但是我想说的是他还是有问题的,对于JVM内部的指令重排这段代码还是有非常大的问题,如果你对JVM的指令重排不明所以的话,可以看我的这篇博客,相信会让你会对指令重排有一个初步的认识的

点击进入 JVM编译之指令重排

在JVM中 instance = new DateBaseTools();这段代码实际上经历了三个指令才完成的

memory =allocate();    //1:分配对象的内存空间 
ctorInstance(memory);  //2:初始化对象 
instance =memory;     //3:设置instance指向刚分配的内存地址

但是我们假定有两个线程,第一个线程调用单例模式的getSingInstance() 开始执行1 ->2 -> 3要是这个时候在JVM中执行的是 1 -> 3 ->2,也就是分配好内存空间后,为instance分配内存地址,这个时候线程二抢占cpu资源,执行getSingInstacne发现instance不为空 就会返回instance,这个时候返回的instanc还没进行初始化,肯定会报错了

所以我们只需要在instance的后面加上volatile关键字,volatile关键字有一个作用就是防止JVM对其进行指令重排序
这就是关于懒汉式的写法了,下面来介绍一下饿汉式


饿汉式 :

饿汉式相对于懒汉式简单,在对单例进行引用的时候就对instance进行初始化并且用static final 进行修饰,这样就不会再多线程中造成bug了,下面我们来看一下代码

public class DateBaseTools{
    private static final DateBaseTools instance = new DateBaseTools();

    public static DateBaseTools getSingInstance() {


       return  instance;
    }
    public void action() {
    System.out.println(">>>>>>>>>>");
    }
}

这样看起来要是多线程操作这个单例也就不会出现多次new的操作了,但是这种方法在一些场景是没有办法被使用的,比如我们需要向单例类传递一些值,就需要通关构造函数来传递参数,饿汉式的写法就会在一开就是自己实例化了自己,根本就没有机会传递参数进去

总结 :

饿汉式也好懒汉式也好都有自己不同的使用场景关于单例模式还有很多种不同的写法,比如说静态内部类、枚举这些方法就不一一列举了,一法通则万法通,这些方法都离不开本质,关于单例模式就讲到这里了

1
0
查看评论

java 单例设计模式使用场景

单例设计模式的使用场景说明,优缺点,在线程中该使用那种方式的单例,其中涉及到类在JVM中的分配...
  • bob_Xing_Yang
  • bob_Xing_Yang
  • 2017-05-23 10:53
  • 381

大话设计模式——单例模式

宏观导图 细节展示  单例模式的结构图: 关键代码:  在GetInstance方法中,要加入判断。 if (instance==null) { instance=new Singleton(); } 对比学习:   单例模式VS实用类的静态方法 ...
  • u011500356
  • u011500356
  • 2015-01-05 21:44
  • 1459

Android面试设计模式之单例模式

在面试的时候面试官会问我们常用的一些设计模式,这里先介绍一下单例模式。 为什么要使用单例模式 1.控制资源的使用,通过线程同步来控制资源的并发访问; 2.控制实例产生的数量,达到节约系统资源; 3.作为通讯媒介使用,也就是数据共享,它可以在不建立直接关联的条件下,让多个不相关的两个线程或者进程之...
  • qq_435559203
  • qq_435559203
  • 2016-09-12 18:25
  • 577

设计模式之——单例模式(Singleton)的实现、优缺点和常见应用场景

一.单例模式的三种实现 以及各自的优缺点 转载自http://my.oschina.net/suyewanwan/blog/102525 单例模式:单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。 单例模式有三种:懒...
  • qq_30465427
  • qq_30465427
  • 2015-08-09 15:36
  • 1197

设计模式——单例模式(Java)——考虑多线程环境下的线程安全问题

设计模式——单例模式(Java)——考虑多线程环境下的线程安全问题 一:单例模式概念 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例 二:单例模式的实现方式 特别注意,在多线程环境下,需要对获取对象实例的方法加对象锁(...
  • ly969434341
  • ly969434341
  • 2016-05-10 21:38
  • 2663

java设计模式之单例模式(几种写法及比较)

概念:   java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。   单例模式有以下特点:   1、单例类只能有一个实例。   2、单例类必须自己创建自己的唯一实例。   3、单例类必须给所有其他对象提供这一实例。   单...
  • tolcf
  • tolcf
  • 2015-10-21 22:56
  • 6268

浅谈JAVA设计模式之——单例模式(Singleton)

一、概述        保证一个类仅有一个实例,并提供一个访问它的全局访问点。 二、适用性      1.当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。   ...
  • l1028386804
  • l1028386804
  • 2015-05-02 17:50
  • 1081

Java四种单例设计模式

Java中的四种单例模式单例模式是最容易理解的设计模式之一,介绍Java中单例模式的四种写法。1.饿汉式单例模式public class Singleton{ private static Singleton instance=new Singleton(); private Sin...
  • twocold_2010
  • twocold_2010
  • 2016-11-20 15:13
  • 264

Java设计模式—单例模式

单例模式介绍: 单例模式(Singleton Pattern)是Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需...
  • ning421479924
  • ning421479924
  • 2017-06-11 01:13
  • 118

浅谈常见设计模式--单例模式 简单工厂模式

今题那站在这里和大家一起分享最近在一本书上看到的关于设计模式的内容,接下来要讲的设计模式有: 单例模式 简单工厂模式 工厂方法和抽象工厂 代理模式 命令模式 策略模式 门面模式 桥接模式 观察者模式 接下来详细介绍灭一种设计模式(注意:下面的讲解都是基于java语言)1.单例模式 定义:Java...
  • xikai18827083487
  • xikai18827083487
  • 2016-11-13 17:04
  • 966
    个人资料
    • 访问:112200次
    • 积分:1387
    • 等级:
    • 排名:千里之外
    • 原创:41篇
    • 转载:3篇
    • 译文:0篇
    • 评论:32条
    博客专栏
    最新评论