Java设计模式------单例模式
单例模式
简单介绍
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
比如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设计模式------单例模式相关推荐
- Java 设计模式 - 单例模式
Java 设计模式 - 单例模式 作者: 霍英俊 [huo920@live.com] 文章目录 Java 设计模式 - 单例模式 单例设计模式介绍 单例设计模式八种方式 饿汉式 - 静态常量 饿汉式( ...
- Java设计模式——单例模式
单例模式 1.什么是单例模式. 2.单例设计的几种实现方式. 2.1.懒汉式 2.2.饿汉式 2.3.登记式 3.总结 4.建议 1.什么是单例模式. 确保某一个类只有一个实例,并且提供一个全局访问点 ...
- Java设计模式——单例模式的七种写法
单例模式(Singleton) 单例模式(Singleton)是一种常用的设计模式.在Java应用中,单例模式能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处: 1.某些类创建比较频 ...
- JAVA设计模式--单例模式
Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 核心知识点如下: (1) 将采用单例设计模式的类的 ...
- 我的Java设计模式-单例模式
就算不懂设计模式的兄弟姐妹们,想必也听说过单例模式,并且在项目中也会用上.但是,真正理解和熟悉单例模式的人有几个呢?接下来我们一起来学习设计模式中最简单的模式之一--单例模式 一.为什么叫单例模式? ...
- JAVA设计模式 - 单例模式
单例模式(Singleton)是软件设计中一种比较常见的 , 相对简单的设计模式 . 1 . 单例模式的定义 所谓单例 , 指的就是单示例 , 即某个类的实现对象有且仅能有一个 , 并提供对外调用的方 ...
- java设计模式—单例模式
(一)单例模式 java中一共有23种设计模式 : 是开发人员根据不同的代码场景总结出来的不同的实现方法, 归纳为23种代码的设计方法, 单例模式就是其中的一种. 单例模式 : 在整个系统中,一个类 ...
- 【文末抽书】Java设计模式--单例模式
来源 :投稿 | 作者 : gyl-coder|原文:阅读原文 在介绍单例模式之前,我们先了解一下,什么是设计模式? 设计模式(Design Pattern):是一套被反复使用,多数人知晓的,经过分类 ...
- java设计模式---单例模式
关于单例模式的文章,其实网上早就已经泛滥了.但一个小小的单例,里面却是有着许多的变化.网上的文章大多也是提到了其中的一个或几个点,很少有比较全面且脉络清晰的文章,于是,我便萌生了写这篇文章的念头.企图 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
最新文章
- Cannot input a tensor of dimension other than 0 as a scalar argument
- CentOS 6.3安装(详细图解教程)
- python数据分析师工作内容_数据分析师日常工作是什么?
- Linux多线程编程(不限Linux)
- 如何通过 C# 比较两幅图片的相似度?
- Docker(二十三)-Docker使用pipework配置本地网络
- 利用Kubernetes名称空间来管理内存和CPU资源(二)
- 简单叙述tcp/ip的工作原理和主要的协议_802.11协议精读3:CSMA/CD与CSMA/CA
- 编程疑难杂症の真的非常一样的文本?!
- python装饰器Decorators
- 火狐浏览器Firefox怎样设置中文
- 手机隐藏Magisk的root痕迹,适用于含zygisk的Magisk
- 如何快速抠图?图片怎样去底色变透明?
- DTOI 10.24 测试(被爆屠) orz IcePrincess_1968
- android开发发送短信,Android开发入门之发送短信
- 物理环路造成网络风暴,无法访问交换机故障处理过程
- 1031 查验身份证 PTA
- 基于深度学习的图像超分辨率方法 总结
- AD9361 介绍 (下)
- 教你利用python画画
热门文章
- python 持续集成 教程_dotnet 部署 github 的 Action 进行持续集成|简明python教程|python入门|python教程...
- Javascript:创建对象的方式
- SQL:解决PostgreSQL数据库传输出现ERROR: invalid byte sequence for encoding “UTF8“: 0xe5 0x9b 0x20
- 关于唐杰老师18年KDD-DeepInf-社会影响力论文初次理解《DeepInf: Social Influence Prediction with Deep Learning》
- vector容器中是否应该放指针?解决方法
- 优先队列及BFS应用
- 近期关于感知器MLP的最新研究
- 图像/视频超分之降质过程
- 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。
- maven安装和eclipse集成