单例模式

简单介绍

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,-般情况下,一个项目通常只需要一个SessionFactory就够,这是就会使用到单例模式。

单例模式有八种方式:

1)饿汉式(静态常量)
2)饿汉式(静态代码块)
3) 懒汉式(线程不安全)
4)懒汉式(线程安全,同步方法)
5)懒汉式(线程安全,同步代码块)
6) 双重检查
7)静态内部类
8)枚举

饿汉式

静态常量写法

1)构造器私有化(防止 new )
2)类的内部创建对象I
3) 向外暴露一个静态的公共方法

代码

/*** @author 王庆华* @version 1.0* @date 2020/12/20 15:59* @Description TODO* @pojectname 饿汉式(静态变量写法)*/
public class SingletonTest01 {public static void main(String[] args) {//测试Singleton instance = Singleton.getInstance();Singleton instance1 = Singleton.getInstance();System.out.println(instance==instance1);System.out.println("instance的hashcode"+instance.hashCode());System.out.println("instance1的hashcode"+instance1.hashCode());}
}
class Singleton{//1.构造器私有,让外部不能newprivate Singleton(){}//2.本类内部创建对象实例private final static Singleton instance = new Singleton();//3.对外提供一个共有的静态方法,返回实例对象public static Singleton getInstance(){return instance;}
}
true
instance的hashcode1836019240
instance1的hashcode1836019240

肯定是同一个对象,因为两次调用getInstance返回的都是同一个instance对象

优缺点

**优点:**这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:

在类装载的时候就完成实例化,没有达到Lazy Loading(懒加载)的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费

这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果

静态代码块写法

代码

/*** @author 王庆华* @version 1.0* @date 2020/12/20 15:59* @Description TODO* @pojectname 饿汉式(静态代码块写法)*/
public class SingletonTest01 {public static void main(String[] args) {//测试Singleton instance = Singleton.getInstance();Singleton instance1 = Singleton.getInstance();System.out.println(instance==instance1);System.out.println("instance的hashcode"+instance.hashCode());System.out.println("instance1的hashcode"+instance1.hashCode());}
}class Singleton{//1.构造器私有,让外部不能newprivate Singleton(){ }//2.本类内部创建对象实例private static Singleton instance;static {//在静态代码块中创建单例对象instance = new Singleton();}//3.对外提供一个共有的静态方法,返回实例对象public static Singleton getInstance(){return instance;}
}

这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
结论:这种单例模式可用,但是可能造成内存浪费

懒汉式

线程不安全写法

代码

/*** @author 王庆华* @version 1.0* @date 2020/12/20 16:17* @Description TODO* @pojectname 线程不安全的懒汉式写法*/
public class SingletonTest03 {public static void main(String[] args) {//测试Singleton instance = Singleton.getInstance();Singleton instance1 = Singleton.getInstance();System.out.println(instance==instance1);System.out.println("instance的hashcode"+instance.hashCode());System.out.println("instance1的hashcode"+instance1.hashCode());}
}
class Singleton{private static Singleton instance;//私有化构造方法private Singleton(){}//提供一个静态的公有方法,当时用到该方法时,才去创建instancepublic static Singleton getInstance(){if (instance == null){instance = new Singleton();}return instance;}
}

优缺点

起到了Lazy Loading的效果,但是只能在单线程下使用。
如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式

结论:在实际开发中,不要使用这种方式.

同步方法

上面的不安全写法问题告诉我们,主要问题在于getInstance的时候,会存在多线超,同时进入导致不安全,那么我们就想办法处理,让这个办法变安全

代码

package com.wang.singleton.type4;
/*** @author 王庆华* @version 1.0* @date 2020/12/20 16:17* @Description TODO* @pojectname 线程安全的懒汉式写法*/
public class SingletonTest03 {public static void main(String[] args) {//测试Singleton instance = Singleton.getInstance();Singleton instance1 = Singleton.getInstance();System.out.println(instance==instance1);System.out.println("instance的hashcode"+instance.hashCode());System.out.println("instance1的hashcode"+instance1.hashCode());}
}
class Singleton{private static Singleton instance;//私有化构造方法private Singleton(){}//提供一个静态的公有方法,当时用到该方法时,才去创建instance//使用synchronized关键字 同步处理代码,解决线程安全问题public static synchronized Singleton getInstance(){if (instance == null){instance = new Singleton();}return instance;}
}

优缺点

解决了线程不安全问题

效率太低了,每个线程在想获得类的实例时候**,执行getInstance()方法都要进行同步**。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低
结论:在实际开发中,不推荐使用这种方式

同步代码块

那上面同步方法的写法效率低,那我们怎么解决这个效率低的问题

代码

package com.wang.singleton.type5;
/*** @author 王庆华* @version 1.0* @date 2020/12/20 16:17* @Description TODO* @pojectname 线程安全的懒汉式写法(同步代码块)*/
public class SingletonTest03 {public static void main(String[] args) {//测试Singleton instance = Singleton.getInstance();Singleton instance1 = Singleton.getInstance();System.out.println(instance==instance1);System.out.println("instance的hashcode"+instance.hashCode());System.out.println("instance1的hashcode"+instance1.hashCode());}
}
class Singleton{private static Singleton instance;//私有化构造方法private Singleton(){}//提供一个静态的公有方法,当时用到该方法时,才去创建instance//使用synchronized关键字 同步处理代码,解决线程安全问题public static  Singleton getInstance(){if (instance == null){synchronized (Singleton.class) {instance = new Singleton();}}return instance;}
}

优缺点

这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低,改为同步产生实例化的的代码块
但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句这时便会产生多个实例
结论:在实际开发中,不能使用这种方式

单例模式

双重检测锁模式

又叫DCL懒汉式

代码

/*** @author 王庆华* @version 1.0* @date 2020/12/20 16:17* @Description TODO* @pojectname 双重检查写法 DCL*/
public class SingletonTest03 {public static void main(String[] args) {//测试Singleton instance = Singleton.getInstance();Singleton instance1 = Singleton.getInstance();System.out.println(instance==instance1);System.out.println("instance的hashcode"+instance.hashCode());System.out.println("instance1的hashcode"+instance1.hashCode());}
}class Singleton{//使用volatile保证对内存的及时可见性private static volatile Singleton instance;//私有化构造方法private Singleton(){}//提供一个静态的公有方法,当时用到该方法时,才去创建instance//使用synchronized关键字 同步处理代码,解决线程安全问题public static Singleton getInstance(){if (instance == null){synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

我们来分析一下,

假如我们现在处在一个多线程的环境下,有两个比较特别的线程A和B,他俩走到了外层判断 if (instance == null),诶我们这知道,现在AB都没有instance这个实例,所以我们得创建啊,那AB往里面挤,诶,碰到一堵墙synchronized (Singleton.class),锁了这个实例对象(关于锁的是谁我的另一篇博客JUC讲到https://blog.csdn.net/qq_22155255/article/details/109749311),AB谁快谁先进,假如,A先进去了,B在外面等着(注意啊,这个时候B是null,还没实例)A进去又遇到了第二次判断if (instance == null)满足,诶,我进来new了一个实例,这个时候,我们上面的关键字volatile起作用了,instance不为空了,B有实例了,就不走第二层检查中的创建实例对象了

优缺点

Double-Check概念是多线程开发中常使用到的,如代码虫所示,我们进行了两次if (singleton==null)检查,这样就可以保证线程安全了。
这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象,也避免的反复进行方法同步.线程安全;延迟加载;效率较高
结论:在实际开发中,推荐使用这种单例设计模式

静态内部类

静态内部类的特点

当我们的Singleton这个类被装载的时候,我们的静态内部类是不会被装载的,当我们要调用getInstance方法要用到静态变量的时候,静态内部类才会被加载,而且只会装载一次,且不存在线程安全问题

代码

package com.wang.singleton.type7;
/*** @author 王庆华* @version 1.0* @date 2020/12/20 16:17* @Description TODO* @pojectname 使用静态内部类完成单例模式*/
public class SingletonTest03 {public static void main(String[] args) {//测试Singleton instance2 = Singleton.getInstance();Singleton instance3 = Singleton.getInstance();System.out.println(instance2==instance3);System.out.println("instance的hashcode"+instance2.hashCode());System.out.println("instance1的hashcode"+instance3.hashCode());}
}
//静态内部类完成
class Singleton{//使用volatile保证对内存的及时可见性private static volatile Singleton instance;//构造器私有化private Singleton(){}//写一个静态内部类,该类中有一个静态属性Singletonprivate static class SingletonIntsance{private static final Singleton INSTSANCE = new Singleton();}//提供一个静态公有方法,直接返回SingletonIntsance.INSTSANCEpublic static synchronized Singleton getInstance(){return SingletonIntsance.INSTSANCE;}
}

优缺点

1.当我们的Singleton这个类被装载的时候,我们的静态内部类是不会被装载的,当我们要调用getInstance方法要用到静态变量的时候,静态内部类才会被加载保证我们的懒加载

2.利用JVM类的装载机制,只会装载一次实例化一次。

3.类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
结论:推荐使用

枚举方式

代码

package com.wang.singleton.type8;
/*** @author 王庆华* @version 1.0* @date 2020/12/20 16:59* @Description TODO* @pojectname 枚举方式*/
public class SingletonTest08 {public static void main(String[] args) {Singleton instance = Singleton.INSTANCE;Singleton instance2 = Singleton.INSTANCE;System.out.println(instance == instance2);System.out.println("instance1的"+instance.hashCode());System.out.println("instance2的"+instance2.hashCode());instance.sayOK();}
}
enum Singleton{INSTANCE;//属性只有一个,保证单例public void sayOK(){System.out.println("OK~~");}
}

优缺点

这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
这种方式是Effective Java作者Josh Bloch提倡的方式
结论:推荐使用

关于单例模式被反射,反序列化破坏的案例和解决方案在我另一篇博客有写到

https://blog.csdn.net/qq_22155255/article/details/109749311第十七篇单例模式有详细写到

源码

我们JDK中,java.lang .Runtime就是经典的单例模式

public class Runtime {private static Runtime currentRuntime = new Runtime();/*** Returns the runtime object associated with the current Java application.* Most of the methods of class <code>Runtime</code> are instance* methods and must be invoked with respect to the current runtime object.** @return  the <code>Runtime</code> object associated with the current*          Java application.*/public static Runtime getRuntime() {return currentRuntime;}/** Don't let anyone else instantiate this class */private Runtime() {}

看得出来,Runtime用的是我们的饿汉式的单例模式来创建对象的

单例模式的注意事项和细节说明

1)单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系統性能
2)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
3)**单例模式使用的场景:**需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)

Java设计模式------单例模式相关推荐

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

    Java 设计模式 - 单例模式 作者: 霍英俊 [huo920@live.com] 文章目录 Java 设计模式 - 单例模式 单例设计模式介绍 单例设计模式八种方式 饿汉式 - 静态常量 饿汉式( ...

  2. Java设计模式——单例模式

    单例模式 1.什么是单例模式. 2.单例设计的几种实现方式. 2.1.懒汉式 2.2.饿汉式 2.3.登记式 3.总结 4.建议 1.什么是单例模式. 确保某一个类只有一个实例,并且提供一个全局访问点 ...

  3. Java设计模式——单例模式的七种写法

    单例模式(Singleton) 单例模式(Singleton)是一种常用的设计模式.在Java应用中,单例模式能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处: 1.某些类创建比较频 ...

  4. JAVA设计模式--单例模式

    Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 核心知识点如下: (1) 将采用单例设计模式的类的 ...

  5. 我的Java设计模式-单例模式

    就算不懂设计模式的兄弟姐妹们,想必也听说过单例模式,并且在项目中也会用上.但是,真正理解和熟悉单例模式的人有几个呢?接下来我们一起来学习设计模式中最简单的模式之一--单例模式 一.为什么叫单例模式? ...

  6. JAVA设计模式 - 单例模式

    单例模式(Singleton)是软件设计中一种比较常见的 , 相对简单的设计模式 . 1 . 单例模式的定义 所谓单例 , 指的就是单示例 , 即某个类的实现对象有且仅能有一个 , 并提供对外调用的方 ...

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

    (一)单例模式  java中一共有23种设计模式 : 是开发人员根据不同的代码场景总结出来的不同的实现方法, 归纳为23种代码的设计方法, 单例模式就是其中的一种. 单例模式 : 在整个系统中,一个类 ...

  8. 【文末抽书】Java设计模式--单例模式

    来源 :投稿 | 作者 : gyl-coder|原文:阅读原文 在介绍单例模式之前,我们先了解一下,什么是设计模式? 设计模式(Design Pattern):是一套被反复使用,多数人知晓的,经过分类 ...

  9. java设计模式---单例模式

    关于单例模式的文章,其实网上早就已经泛滥了.但一个小小的单例,里面却是有着许多的变化.网上的文章大多也是提到了其中的一个或几个点,很少有比较全面且脉络清晰的文章,于是,我便萌生了写这篇文章的念头.企图 ...

  10. JAVA设计模式-单例模式(Singleton)线程安全与效率

    一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...

最新文章

  1. Cannot input a tensor of dimension other than 0 as a scalar argument
  2. CentOS 6.3安装(详细图解教程)
  3. python数据分析师工作内容_数据分析师日常工作是什么?
  4. Linux多线程编程(不限Linux)
  5. 如何通过 C# 比较两幅图片的相似度?
  6. Docker(二十三)-Docker使用pipework配置本地网络
  7. 利用Kubernetes名称空间来管理内存和CPU资源(二)
  8. 简单叙述tcp/ip的工作原理和主要的协议_802.11协议精读3:CSMA/CD与CSMA/CA
  9. 编程疑难杂症の真的非常一样的文本?!
  10. python装饰器Decorators
  11. 火狐浏览器Firefox怎样设置中文
  12. 手机隐藏Magisk的root痕迹,适用于含zygisk的Magisk
  13. 如何快速抠图?图片怎样去底色变透明?
  14. DTOI 10.24 测试(被爆屠) orz IcePrincess_1968
  15. android开发发送短信,Android开发入门之发送短信
  16. 物理环路造成网络风暴,无法访问交换机故障处理过程
  17. 1031 查验身份证 PTA
  18. 基于深度学习的图像超分辨率方法 总结
  19. AD9361 介绍 (下)
  20. 教你利用python画画

热门文章

  1. python 持续集成 教程_dotnet 部署 github 的 Action 进行持续集成|简明python教程|python入门|python教程...
  2. Javascript:创建对象的方式
  3. SQL:解决PostgreSQL数据库传输出现ERROR: invalid byte sequence for encoding “UTF8“: 0xe5 0x9b 0x20
  4. 关于唐杰老师18年KDD-DeepInf-社会影响力论文初次理解《DeepInf: Social Influence Prediction with Deep Learning》
  5. vector容器中是否应该放指针?解决方法
  6. 优先队列及BFS应用
  7. 近期关于感知器MLP的最新研究
  8. 图像/视频超分之降质过程
  9. 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。
  10. maven安装和eclipse集成