单例模式,简而言之就是在整个应用程序里面有且仅有一个实例,在程序的任何时候,任何地方获取到的该对象都是同一个对象。单例模式解决了一个全局的类被频繁创建和销毁的,或者每次创建或销毁都需要消耗大量cpu资源的对象的问题。单例模式总的可以分为懒汉模式和饿汉模式,顾名思义,懒汉模式是一个非常懒的汉子,只要你没有使用到它,它就永远不会实例化。饿汉模式的意思就是,汉子非常饥渴,只要在程序的编译阶段就给你分配内存,创建好对象。

  将懒汉模式和饿汉模式细分,又可以分为:

  1、懒汉模式

  2、饿汉模式

  3、双检模式

  4、静态内部类模式

  5、枚举模式

  不管是用哪一种方式实现的单例模式,其创建流程基本都是一直的:首先将构造方法声明为private的,这样就防止直接new出一个新的对象。第二,声明一个私有的成员变量,即单例对象。第三步,声明一个public的静态方法,用于获取或创建单例对象,外部想要获取该对象必须通过这个方法获取。

  一、懒汉模式1--线程安全

/**
* 饿汉模式1
*/
public class HungrySingleton1 { private static HungrySingleton1 singleton = new HungrySingleton1(); private HungrySingleton1(){} public static HungrySingleton1 getInstance() {
return singleton;
} }

  这种懒汉模式的优点是实现非常简单。缺点是并起到懒加载的效果,如果项目没有使用到这个对象的就会造成资源的浪费。

  二、饿汉模式1--线程不安全

/**
* 懒汉模式1
*/
public class LazySingleton1 { private static LazySingleton1 singleton; private LazySingleton1(){} public static LazySingleton1 getInstance() {
if (singleton == null) {
singleton = new LazySingleton1();
}
return singleton;
} }

  这种写法虽然实现了懒加载的效果,但是严格意义上并不是单例模式,因为在多线程的环境下有可能会创建出多个不同的对象,至于为什么,不懂的可以看一下我之间写的关于Java内存模型的文章。这种写法只能应用于单线程的环境下,局限性很大。实际中强烈不建议使用这种方法。

  三、懒汉模式2--线程安全

  

/**
* 懒汉模式2
*/
public class LazySingleton2 { private static LazySingleton2 singleton; private LazySingleton2(){} public static synchronized LazySingleton2 getInstance() {
if (singleton == null) {
singleton = new LazySingleton2();
}
return singleton;
} }

  这种写法咋看跟上面的方法一样,这种写法在方法上添加了 synchronized  关键字,这样就保证了每次只能有一个线程进入方法体中,解决了懒汉模式1中出现的问题。这种写法的优点是实现了懒加载的效果,缺点是效率非常低,当多个线程同时获取实例时,有可能会造成线程阻塞的情况。不推荐使用。

  懒汉模式3--线程不安全

/**
* 懒汉模式3
*/
public class LazySingleton3 { private static LazySingleton3 singleton; private LazySingleton3(){} public static LazySingleton3 getInstance() {
if (singleton == null) {
synchronized (LazySingleton3.class) {
if (singleton == null) {
singleton = new LazySingleton3();
}
}
}
return singleton;
}
}

  这种写法进行了两次 singleton == null 的判断,在实际的应用中当我们调用这个方法时,其实99%的几率是实例就已经创建好了,因此第一个 singleton == null 能过滤掉99%的调用,不用将方法锁起来,从而提高了效率。这种方法的优点是实现懒加载的效果,效率和很高。缺点是代码设计仍然后缺陷,jvm在为对象分配内存和赋值并不是一个原子操作,即 singleton = new LazySingleton3() 这段代码在jvm中是由三个步骤实现的,首先jvm会在堆中为对象分配一定的内存空间,然后完成对象的初始化工作,然后将内存地址指向到对象中。但是,我们知道,jvm在编译的时候并不总是根据我们编写的代码的顺序来执行了,而是根据jvm觉得最优的顺序执行(这个过程就叫做指令重排序),所以有可能在执行了步骤1后就执行了步骤3,这时候第二个线程进来的发现singleton并不为空,因此就直接返回了该对象,因此造成空指针异常。

  四、双重检查锁模式---线程安全

/**
* 懒汉模式4
*/
public class LazySingleton4 { private volatile static LazySingleton4 singleton; private LazySingleton4(){} public static LazySingleton4 getInstance() {
if (singleton == null) {
synchronized (LazySingleton4.class) {
if (singleton == null) {
singleton = new LazySingleton4();
}
}
}
return singleton;
}
}

  相较于上面的方式,这种方式只是在成员变量中添加了 volatile  关键字,解决了指令重排序的问题,同时确保当前线程修改了这个变量时,其他的线程能够及时读到最新的值。这种方法缺点是写起来比较复杂,要求程序员对jvm比较理解。优点是既保证了线程安全,同时也能够保证了比较高的效率。

  五、静态内部类模式--线程安全

/**
* 懒汉模式5
*/
public class LazySingleton5 { private LazySingleton5(){} private static class Holder {
private static final LazySingleton5 INSTANCE = new LazySingleton5();
} public static LazySingleton5 getInstance() {
return Holder.INSTANCE;
} }

  这种写法实现比较简单,即实现了懒加载的效果,同时也保证的多线程环境下的线程安全问题。推荐使用这种方式。

  六、枚举模式 -- 线程安全

  

/**
* 懒汉模式6
*/
public enum LazySingleton6 { INSTANCE } //使用方法
public class Test {

    public static void main(String[] args) {
LazySingleton6 instance = LazySingleton6.INSTANCE;
LazySingleton6 instance1 = LazySingleton6.INSTANCE;
System.out.println(instance == instance1); } }

  推荐写法,简单高效。充分利用枚举类的特性,只定义了一个实例,且枚举类是天然支持多线程的。

  喜欢我写的博客的同学可以关注订阅号【Java解忧杂货铺】,里面不定期发布一些技术干活,也可以免费获取大量最新最流行的技术教学视频

  

设计模式(Java语言)-单例模式的更多相关文章

  1. 设计模式 - Java中单例模式的6种写法及优缺点对比

    目录 1 为什么要用单例模式 1.1 什么是单例模式 1.2 单例模式的思路和优势 2 写法① - 饥饿模式 2.1 代码示例 2.2 优缺点比较 3 写法② - 懒惰模式 3.1 代码示例 3.2 ...

  2. 10.Java设计模式 工厂模式,单例模式

    Java 之工厂方法和抽象工厂模式 1. 概念 工厂方法:一抽象产品类派生出多个具体产品类:一抽象工厂类派生出多个具体工厂类:每个具体工厂类只能创建一个具体产品类的实例. 即定义一个创建对象的接口(即 ...

  3. Java设计模式之《单例模式》及应用场景

    摘要: 原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6510196.html 所谓单例,指的就是单实例,有且仅有一个类实例,这个单例不应该 ...

  4. Java设计模式之【单例模式】

    Java设计模式之[单例模式] 何为单例 在应用的生存周期中,一个类的实例有且仅有一个 当在一些业务中需要规定某个类的实例有且仅有一个时,就可以用单例模式 比如spring容器默认初始化的实例就是单例 ...

  5. Java设计模式中的单例模式

    有时候在实际项目的开发中,我们会碰到这样一种情况,该类只允许存在一个实例化的对象,不允许存在一个以上的实例化对象,我们将这种情况称为Java设计模式中的单例模式.设计单例模式主要采用了Java的pri ...

  6. 设计模式(Java语言)- 工厂方法模式

    前言 在介绍工厂方法模式之前,我们需要知道这个设计模式是什么,解决了什么样的问题?在上一篇博客 设计模式(Java语言)- 简单工厂模式 介绍了简单工厂模式,然后总结了简单工厂模式的缺点: 1.当新增 ...

  7. 设计模式(Java语言)- 原型模式

    原型模式(Prototype Pattern)也有人将原型模式称为克隆模式,是属于创造型设计模式,用于创建重复的对象,提供了一种创建对象的最佳方式.原型模式需要实现Cloneable接口,来实现对象的 ...

  8. java 之 单例模式(大话设计模式)

    笔者记得去面试时曾被问起这个模式,当时还没有看过设计模式,对设计模式基本上一无所知,不过可以肯定的是笔者用过单例模式.当时回答的风马牛不相及,很尴尬. 也是从那时起,开始学习设计模式.今天所说的就是单 ...

  9. 2019 Android 高级面试题总结 从java语言到AIDL使用与原理

    说下你所知道的设计模式与使用场景 a.建造者模式: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 使用场景比如最常见的AlertDialog,拿我们开发过程中举例,比如C ...

  10. 六、java基础-单例模式_继承_覆盖_多态

    1.单例模式: 1)提出原因 是由gof 也就是四人组提出来的.为了保证jvm中某一类型的java对象永远只有一个,同时也是为了节省内存的开销.因为外面程序可以通过new的方法直接调用类里面的构造方法 ...

随机推荐

  1. Podman 使用指南

    原文链接:Podman 使用指南 Podman 原来是 CRI-O 项目的一部分,后来被分离成一个单独的项目叫 libpod.Podman 的使用体验和 Docker 类似,不同的是 Podman 没 ...

  2. Unreal Engine 4 系列教程 Part 2:蓝图教程

    .katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...

  3. python requests自动化框架

    一.项目结构 1.新建一个工程(一定要创建工程),工程名称自己定义,如:yoyo_jiekou 2.在工程的跟目录新建一个脚本:run_main.py,用来执行全部用例 3.在工程下创建以下几个pak ...

  4. 九、Executor框架

    Executor框架 ​ 我们知道线程池就是线程的集合,线程池集中管理线程,以实现线程的重用,降低资源消耗,提高响应速度等.线程用于执行异步任务,单个的线程既是工作单元也是执行机制,从JDK1.5开始 ...

  5. 记个mimikatz小坑

    今晚回学校无聊搞搞自己school  实战的时候遇到mimikatz抓密码报错  以前没遇过 记一下(水一篇) 爆ERROR kuhl_m_privilege_simple ; RtlAdjustPr ...

  6. 自学maya三月,为啥还是95%都还不会,那是因为你不懂这几个技巧

    有一些学员经常会有这种疑问,为什么学习MAYA软件这么难,为什么自己怎么学都学不会? 结果调查,发现了下面几个问题. 游戏建模 第一: 走弯路 很多人一开始学习Maya的时候肯定第一步是安装软件,但是 ...

  7. 在Hadoop上用Python实现WordCount

    一.简单说明 本例中我们用Python写一个简单的运行在Hadoop上的MapReduce程序,即WordCount(读取文本文件并统计单词的词频).这里我们将要输入的单词文本input.txt和Py ...

  8. std::this_thread::yield/sleep_for

    std::this_thread::yield(): 当前线程放弃执行,操作系统调度另一线程继续执行.. std::this_thread::sleep_for(): 表示当前线程休眠一段时间,休眠期 ...

  9. vue —— 监听

    vue的监听用途很大 比如:通过数据的值的变化,执行某个方法 首先:data中要有个变量初始值 finalTotalAmount的初始值是0 我们想当finalTotalAmount值发生变化时,执行 ...

  10. Springboot项目的jar包目录结构

    上图为一个由Luyten工具反编译后的一个jar包 说明: SpringBoot提供的bootstrap的类是放到包的最外面,比如上面的org.springframework.boot.loader. ...