一、什么是单例模式

单例模式常用于创建一个全局共享的类实例,即在整个应用程序中只存在一个类实例

二、单例模式的优点和适用场景

优点:

  1. 在内存中只有一个对象,节省内存空间

  2. 避免频繁的创建销毁对象,可以提高性能

  3. 避免对共享资源的多重占用

  4. 可以全局访问

适用场景:

  1. 需要频繁实例化然后销毁的对象。

  2. 创建对象时耗时过多或者消耗资源过多,但又经常用到的对象。

  3. 有状态的工具类对象。
  4. 频繁访问数据库或文件的对象。

应用场景举例:

  1. 比如Windows系统的任务管理器,不管打开多少次任务管理器,只会弹出一个窗口。如果不使用单例机制,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符。

三、使用时注意事项

使用时不能用反射模式创建单例,否则会实例化一个新的对象
使用懒汉式单例模式时注意线程安全问题
饿汉式单例和懒汉式单例构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)

四、实现思想

将类的构造方法私有化,用一个私有的类变量instance保存类的实例,可以在类加载时创建类的唯一实例,也可以在第一次调用getInstance方法时创建类的唯一实例,再提供一个公有的静态方法getInstance,用于获取类的唯一实例,该方法返回instance。
单例模式的实现可以分为两大类——懒汉式和饿汉式,他们的区别在于:
懒汉式:指全局的类实例在第一次使用时构建
饿汉式:指全局的类实例在类装载时构建
通常我们使用懒汉式的单例模式

五、具体实现

饿汉模式

package 饿汉;/*** Created by MXi4oyu on 2019/7/5.* 特点:线程安全(JVM能够保证在类加载的时候免疫许多由多线程引起的问题)* 获取类实例不用考虑加锁,对应的就效率高,但是不能延时加载,不管用不用这个对象,都会实例化,因此可能会造成资源浪费*/
public class Singleton {//类加载时初始化对象private static Singleton instance = new Singleton();//构造器私有private Singleton(){}//通过静态方法获取实例对象public static Singleton getInstance(){return instance;}//测试是否线程安全public static void main(String[] args){Thread2 [] threadArray = new Thread2[10];for(int i=0;i<threadArray.length;i++){threadArray[i] = new Thread2();threadArray[i].start();}}
}class Thread2 extends Thread{@Overridepublic void run(){System.out.println(Singleton.getInstance().hashCode());}
}

懒汉模式

package 懒汉;/*** Created by MXi4oyu on 2019/7/5.* 特点:线程安全(必修加锁,不然当instance==null的时候两个线程可以并发的进入if语句从而会创建多个对象,违反了单例初衷)* 执行效率低,因为在任何时候只能有一个线程调用 getInstance() 方法。* 可以延时加载,真正用的时候再创建对象(资源利用率高)* 但是同步操作只需要在第一次调用时才被需要,即第一次创建单例实例对象时。这就引出了双重检验锁。*/
public class Singleton {//类加载的时候不初始化对象,什么时候用什么时候初始化private static Singleton instance;private Singleton(){}//加同步锁保证线程安全,调用效率低,真正用的时候才创建对象,延时加载public static synchronized Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;}//测试是否线程安全public static void main(String[] args){Thread2 [] threadArray = new Thread2[10];for(int i=0;i<threadArray.length;i++){threadArray[i] = new Thread2();threadArray[i].start();}}
}class Thread2 extends Thread{@Overridepublic void run(){System.out.println(Singleton.getInstance().hashCode());}
}

双重检测锁

package 双重检测锁;/*** Created by MXi4oyu on 2019/7/5.* 双重检验锁模式(double checked locking pattern),是一种使用同步块加锁的方法。* 程序员称其为双重检查锁,因为会有两次检查 instance == null,一次是在同步块外,一次是在同步块内。* 为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例了。* 特点:线程安全(双重检测判断),效率高(因为这里只需要只第一次创建对象时同步,一旦创建成功,以后获取实例的时候就不需要再同步了),* 可以延时加载,只有真正在调用的时候才创建对象。* Volatile关键字语义:当一个变量定义为volatile之后,它将具备两种特性,第一是保证此变量对所有线程的可见性;第二是禁止指令重排序优化。双重检测锁在jdk1.5之前理论上的实现想法是完美的,但是实际上是行不通的,这是由于java的内存模型有一个指令重排序机制,可能会导致一个已存在却不完整的instance实例对象。JVM在创建对象时是一个非原子性操作,通过new关键字创建对象可以分为三个步骤:1.为instance分配内存空间;2.利用构造器初始化对象;3.将instance引用指向刚分配的内存地址(执行完这步instance就为非空了)。这个过程可能发生指令重排序,也就是说上面三个步骤可能会打乱顺序,但不是说指令任意重排序,CPU需要正确处理指令依赖情况以保证程序能得出正确的执行结果,在当前情况下,指令2依赖于指令1,所以1,2的顺序不可能变,但是指令3并不依赖于指令2,所以可能会出现这样一种情况,执行1之后,再执行3,最后才执行2。这种情况不仅是可能的,而是有一些JIT编译器真实发生的现象。了解了指令重排机制之后,我们再回头看上面代码,如果没有volatile关键字,当线程A执行new Singleton()创建对象,并且将instance引用指向这个对象在内存的地址(这时instance非空),但是Singleton构造函数并没有执行,也就是说步骤1,3已经执行完毕,步骤2还没有执行,同时线程A被线程B占领,此时B得到的会是一个不完整的对象(未被初始化的对象),判断不为空,直接return instance,从而导致系统崩溃。Volatile屏蔽指令重排序的语义是在jdk1.5的时候才完全修复,此前即使将变量定义为volatile也不能完全避免重排序所导致的问题,这点也是在jdk1.5之前的java中无法安全的使用双重检测锁来实现单例模式的原因。*/
public class Singleton {//加上volatile关键字禁止java内存模型的指令重排序机制private static volatile Singleton instance =null;private Singleton(){}//只对需要锁的部分代码加锁public static Singleton getInstance(){if(instance==null){//只需要第一次创建对象的时候加同步锁,执行效率高synchronized (Singleton.class){//双重判断,并发情况下保证只有一个实例if(instance==null){instance = new Singleton();}}}return instance;}//测试是否线程安全public static void main(String[] args){Thread2 [] threadArray = new Thread2[10];for(int i=0;i<threadArray.length;i++){threadArray[i] = new Thread2();threadArray[i].start();}}
}class Thread2 extends Thread{@Overridepublic void run(){System.out.println(Singleton.getInstance().hashCode());}
}

静态内部类

package 静态内部类;import java.security.Signature;/*** Created by MXi4oyu on 2019/7/5.* 特点:延时加载(加载外部类的时候并不会加载内部类,真正调用getInstance()方法的时候才会加载),* 线程安全,执行效率高*/
public class Singleton {//加载Singleton类的时候不会加载静态内部类,可以实现延时加载private static class Inner{//静态内部类维护一个实例对象private static Singleton instance = new Singleton();}private Singleton(){}//类加载的时候天然线程安全,不用同步锁,调用效率高public static Singleton getInstance(){return Inner.instance;}//测试是否线程安全public static void main(String[] args){Thread2 [] threadArray = new Thread2[10];for(int i=0;i<threadArray.length;i++){threadArray[i] = new Thread2();threadArray[i].start();}}
}class Thread2 extends Thread{@Overridepublic void run(){System.out.println(Singleton.getInstance().hashCode());}
}

枚举

package 枚举;/*** Created by MXi4oyu on 2019/7/5.* 特点:简洁(利用枚举独特机制可以很简单的实现单例),线程安全(枚举底层是由static修饰的,静态资源初始化过程也是天然安全的),* 可以防止反射和序列化来破坏单例机制。不能延时加载(静态资源在类加载的时候就会自动加载)*** 总结:一般来说,单例模式有五种写法:懒汉、饿汉、双重检验锁、静态内部类、枚举。通过以上几种实现方式,我们可以知道在运用单例模式往往要考虑以下几个性能:1,是否能够延时加载,充分利用资源2,是否线程安全3,并发情况下的访问性能4,是否可以防止反射和反序列化漏洞**/
public class Singleton {public enum SingletonEnum{//声明一个枚举对象,枚举本身就是单例INSTANCE;}
}

Java单例模式的5种实现方法相关推荐

  1. Java单例模式的几种实现方式

    Java单例模式的几种实现方式 在Java 中,单例类只能有一个实例,必须创建自己的唯一实例,单例类必须给所有其他对象提供这一实例.Java 单例模式有很多种实现方式,在这里给大家介绍单例模式其中的几 ...

  2. python最简单单例模式_Python单例模式的4种实现方法 | 学步园

    Python单例模式的4种实现方法: 方法1: 实现__new__方法,并将一个类的实例绑定到类变量_instance上.如果cls._instace为None,说明该类还未实例化过,实例化该类,并返 ...

  3. java中的五种排序方法_用Java排序的五种有用方法

    java中的五种排序方法 Java排序快速概述: 正常的列表: private static List VEGETABLES = Arrays.asList("apple", &q ...

  4. 按照姓名升序排序的代码_好程序员Java培训分享Java集合的两种排序方法

    好程序员Java培训分享Java集合的两种排序方法,Java集合的工具类Collections中提供了两种排序的方法,分别是: 1.Collections.sort(List list) 2.Coll ...

  5. java oracle的2种分页方法

    java oracle的2种分页方法 一物理分页: <!-- 分页查询所有的博客信息 --><select id="findBlogs" resultType=& ...

  6. Java数组的三种定义方法

    Java数组的三种定义方法 1.第一种适合不用初始化的数组.数组特别长的时候,不初始化,值都是默认值. 2.第二种定义适合直接初始化数组 3.第三种匿名数组适合直接给方法传入参数时使用 1.第一种适合 ...

  7. 单例模式的5种实现方法及优缺点

    单例模式是GoF23种设计模式中创建型模式的一种,也是所有设计模式中最简单的一种. 单例模式是用来保证系统中某个资源或对象全局唯一的一种模式,比如系统的线程池.日志收集器等.它保证了资源在系统中只有一 ...

  8. java单例模式的七种写法_Java设计模式之单例模式的七种写法

    什么是单例模式? 单例模式是一种常见的设计模式,单例模式的写法有很多种,这里主要介绍三种: 懒汉式单例模式.饿汉式单例模式.登记式单例 . 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类 ...

  9. java 中的几种 通用方法“

    前言 Java中,除了基本的数值类型,其他所有数据类型(包括数组)都是对象. 而Object这个类是所有类的超类,它提供的方法,自然能够使用于它的所有子类(所有非基本数值类型). 本文介绍了Objec ...

最新文章

  1. Kdtree(K-dimension tree)学习
  2. Spring Boot @ConfigurationProperties使用指导
  3. linux操作系统的特点有哪些,LINUX操作系统有哪些概念和特点?
  4. 转载 - 10个基于jQuery实现的漂亮网站赏析
  5. DINO:目标检测benchmark COCO屠榜的正确姿势
  6. jquery将html转为pdf文件,通过Jquery将HTML Div转换为PDF
  7. c语言结构体使用方法
  8. mac地址扫描源码_iNet Network Scanner扫描网络,及时反馈WiFi信号强度!
  9. Android开发(五)——计时器
  10. vue 2.6 插槽v-slot用法记录
  11. 【整理】LISP简介
  12. php CSRF攻击与防御
  13. icem网格划分如何给内部面网格,ICEM CFD处理混合网格划分中低质量的问题
  14. 计算机识别图像的原理,什么是图像识别技术?图像识别技术原理介绍
  15. Django项目实战——11—(文件存储方案FastDFS、容器化方案Docker)
  16. C语言:L1-078 吉老师的回归 (15 分)
  17. 利用vantUI组件库中的Field 输入框、Cell 单元格完成金额数字框的数字转金额格式和金额大写
  18. 苹果a14和骁龙888哪个厉害 苹果a14相当于骁龙多少
  19. CSDN写文章MarkDown用到的表情包收集(转自Github)
  20. Java读取串口数据

热门文章

  1. JAVA导入导出CRV文件
  2. 安卓USB模块源码分析(一)- 准备
  3. 创客SolidWorks - 认识SolidWorks
  4. java用date获取星期几_根据Java 8中的LocalDate.now()获取星期几的日期
  5. Linux学习--如何通过Shell脚本实现发送邮件通知功能?
  6. 我的coding之路--实习第一个月的感悟 和 QT 入门方法
  7. Android耳机线控详解,蓝牙耳机按钮监听(仿酷狗线控效果)
  8. html5+css3基础内容
  9. 手机客户端登录流程及风险说明
  10. WooCommerce入门指南:产品标签,类别和属性