当前位置:   article > 正文

Java学习笔记之--------单例模式(一)_利用单例模式,管理文件中读取的数据,模拟程序的缓存设置。利用`get`方法获取其数

利用单例模式,管理文件中读取的数据,模拟程序的缓存设置。利用`get`方法获取其数

单例模式的核心作用

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

单例模式的应用场景

Windows的Task Manager(任务管理器)。

Windows的Recycle Bin(回收站)。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都new一个对象去读取。

网站的计数器,一般也是采用单例模式实现,否则难以同步。

应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。

操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。

Application也是单例的典型应用。(Servlet编程中会涉及到)

在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理。

在servlet编程中,每个servlet也是单例。

在spring MVC框架/struts框架中,控制器对象也是单例。

单例模式的优点

由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

单例模式可以在系统设置全局的访问点,优化共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

常见的单例模式(五种)

1.饿汉式:线程安全,调用频率高。不能延迟加载。

2.懒汉式:线程安全,调用效率不高。可以延迟加载。

3.双重检测锁式:由于JVM底层内部模型原因,偶尔会出现问题。不建议使用。

4.静态内部类式:线程安全,调用效率高。可以延迟加载。

5.枚举单例:线程安全,调用效率高,不能延迟加载。可以天然的防止反射和反序列化漏洞。

注:延迟加载:当在真正需要数据的时候,才真正执行数据加载操作。可以简单理解为,只有在使用时才会发sql语句进行查询。

单例模式----饿汉式实现:单例对象立即加载

  1. public class SingletonDemo01 {
  2. //类初始化时,立即加载这个对象(没有延时加载的优势)!由于加载类时,天然的是线程安全的!
  3. private static SingletonDemo01 instance = new SingletonDemo01();
  4. private SingletonDemo01(){
  5. }
  6. //方法没有同步,调用效率高!
  7. public static SingletonDemo01 getInstance(){
  8. return instance;
  9. }
  10. }

饿汉式单例模式代码中,static变量会在类加载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。

存在的问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费。

单例模式----懒汉式实现:单例对象延迟加载

  1. public class SingletonDemo02 {
  2. //类初始化时,不初始化这个对象(延迟加载,真正用的时候再创建)。
  3. private static SingletonDemo02 instance;
  4. //私有化构造器
  5. private SingletonDemo02(){
  6. }
  7. //方法同步,调用效率低!
  8. public static synchronized SingletonDemo02 getInstance(){
  9. if (instance == null){
  10. instance = new SingletonDemo02();
  11. }
  12. return instance;
  13. }
  14. }

懒加载,延迟加载,只有真正用的时候才加载。

存在的问题:虽然资源利用率高了,但是每次调用getInstance()方法都要同步,并发效率较低。

单例模式----双重检测锁实现

  1. public class SingletonDemo03 {
  2. private static volatile SingletonDemo03 instance = null;
  3. public static SingletonDemo03 getInstance(){
  4. if (instance == null){
  5. SingletonDemo03 sc;
  6. synchronized (SingletonDemo03.class){
  7. sc = instance;
  8. if (sc == null){
  9. synchronized (SingletonDemo03.class){
  10. if (sc == null){
  11. sc = new SingletonDemo03();
  12. }
  13. }
  14. instance = sc;
  15. }
  16. }
  17. }
  18. return instance;
  19. }
  20. private SingletonDemo03(){
  21. }
  22. }

这个模式将内容同步到下方if内部,提高了执行的效率,不必每次获取对象都进行同步,只有第一次才同步,创建了以后就没必要了。

存在的问题:由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题。不建议使用(实际工作中用不到)。

注意:在1.4以及更早版本的Java中,许多JVM对于volatile关键字的实现会导致双重检查加锁的失效。如果使用的是旧版本的jdk,就不要用这个方法来实现单例模式。

单例模式----静态内部类实现方式

  1. public class SingletonDemo04 {
  2. public static class SingletonClassInstance {
  3. private static final SingletonDemo04 instance = new SingletonDemo04();
  4. }
  5. public static SingletonDemo04 getInstance() {
  6. return SingletonClassInstance.instance;
  7. }
  8. private SingletonDemo04(){
  9. }
  10. }

外部类没有static属性,则不会像饿汉式那样立即加载对象。

只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性。

兼备了并发高效调用和延迟加载的优势。

静态内部类实现方式也是一种懒加载方式。使用较多。

单例模式----使用枚举实现单例模式

  1. public enum SingletonDemo05 {
  2. //这个枚举元素,本身就是单例对象!
  3. INSTANCE;
  4. //添加自己需要的操作
  5. public void singletonOperation(){
  6. }
  7. public static Object INSTANCE() {
  8. return INSTANCE;
  9. }
  10. }

使用枚举实现单例模式,实现简单。枚举本身就是单例模式。由JVM从根本上提供保障。避免通过反射和反序列化的漏洞。

存在的问题:无延迟加载。

各种单例模式的适用场景

当单例对象占用资源较少,不需要延迟加载的时候,枚举实现单例模式优于饿汉式。

当单例对象占用资源较大,需要延迟加载的时候,静态内部类的实现方式优于懒汉式。

单例模式的类图

getInstance()方法是静态的,意味着它是一个类方法,所以可以在代码的任何地方使用SingletonDemo01.getInstance()访问它。和访问全局变量一样,只是这里我们可以延迟实例化。(类图是由idea自带的工具生成的)

 

 

以上为单例模式的学习笔记,此文章为尚学堂视频的学习笔记+自己总结。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/157697
推荐阅读
相关标签
  

闽ICP备14008679号