热门标签 | HotTags
当前位置:  开发笔记 > 开发工具 > 正文

Java设计模式单例模式(Singleton)用法解析

这篇文章主要介绍了Java设计模式单例模式(Singleton)用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

这篇文章主要介绍了Java设计模式单例模式(Singleton)用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

单例模式的应用场景:

  单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例。并提供一个全局反访问点。单例模式是创建型模式。单例模式在生活中应用也很广泛,比如公司CEO只有一个,部门经理只有一个等。JAVA中ServletCOntext,ServetContextCOnfig等,还有spring中ApplicationContext应用上下文对象,SessionFactory,数据库连接池对象等。使用单例模式可以将其常驻于内存,可以节约更多资源。

写法:

  1:懒汉模式(线程不安全)

/**
 * 线程不安全的懒汉式单利模式
 * 
 * Created by gan on 2019/11/17 17:33.
 */
public class LazySingleton {
  private static LazySingleton instance;

  //构造方法私有化
  private LazySingleton() {
  }

  public static LazySingleton getInstance() {
    if (instance != null) {
      instance = new LazySingleton();
    }
    return instance;
  }
}

  上面的代码,提供一个静态对象instance,构造函数私有化防止外部创建对象,提供一个静态的getInstance方法来给访问者一个单例对象。这种写法的缺点就是没有考虑到线程安全问题,当多个访问者同时访问的时候很有可能创建多个对象。之所以叫懒汉式,是因为这种写法是使用的时候才创建,起到了懒加载Lazy loading的作用,实际开发中不建议采用这种写法。

  2:线程安全的懒汉式(加锁)

/**
 * 线程安全的懒汉式单利模式
 * 
 * Created by gan on 2019/11/17 17:33.
 */
public class LazySingleton {
  private static LazySingleton instance;

  //构造方法私有化
  private LazySingleton() {
  }

  public synchronized static LazySingleton getInstance() {
    if (instance != null) {
      instance = new LazySingleton();
    }
    return instance;
  }
}

  这种写法就是在第一种的基础上添加了synchronized关键字保证了线程安全。这种写法在并发高的时候虽然保证了线程安全,但是效率很低,高并发的时候所有访问的线程都要排队等待,所以实际开发中也不建议采用。

  3:恶汉式(线程安全)

/**
 * 饿汉式(线程安全)
 * Created by gan on 2019/10/28 22:52.
 */
public class HungrySigleton {

  public static final HungrySigleton instance = new HungrySigleton();

  private HungrySigleton(){}

  public static HungrySigleton getInstance(){
    return instance;
  }
}

  直接在运行(加载)这个类的时候创建了对象,之后直接访问。显然这种方式没有起到Lazy loading的效果。但是是线程安全的,实际开发中还是比较常用。

  4:静态内部类(线程安全)

/**
 * 静态内部类方式
 * Created by gan on 2019/11/17 17:46.
 */
public class StaticInnerClassSingleton {

  //构造方法私有化
  private StaticInnerClassSingleton() {}

  //内部类
  private static class HolderInnerClass {
    //需要提供单利对象的外部类作为静态属性加载的时候就初始化
    private static StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
  }

  //对外暴漏访问点
  public static StaticInnerClassSingleton getInstance() {
    return HolderInnerClass.instance;
  }
}

  这种内部类跟饿汉式单例有很多相似的地方,相比饿汉式单例模式的区别也是好处在于:静态内部类不在单例类加载时就加载,而是在调用getInstance()方法的时候才进行加载,达到了类似于懒汉式的效果,而且这种方法又是线程安全的。实际开发中也建议采用。

  5:枚举方法单例(线程安全)

/**
 * 枚举单利模式
 * Created by gan on 2019/11/17 17:57.
 */
public enum EnumSingleton {
  INSTANCE;

  public void otherMetthod() {
    System.out.println("需要单利对象调用的方法。。。");
  }
}

  Effective Java作者Josh Bloch提倡的方式,好处有如下:

  1:自由串行化。

  2:保证了一个实例

  3:线程安全

  这种方式防止了单例模式被破坏,而且简洁写法简单,而且绝对的线程安全,但是有个缺点就是不能继承。

  6:双重检查法(通常线程安全,低概率不安全)

/**
 * Double check
 * Created by gan on 2019/11/17 18:03.
 */
public class DoubleCheckSingleton {
  private static DoubleCheckSingleton instance;

  private DoubleCheckSingleton() {}

  public static DoubleCheckSingleton getInstance() {
    if (instance == null) {
      synchronized (DoubleCheckSingleton.class) {
        if (instance == null) {
          instance = new DoubleCheckSingleton();
        }
      }
    }
    return instance;
  }
}

  上面的这种写法在并发极高的时候也可能会出现问题(当然这种概率非常小,但是毕竟还是有的嘛),解决的方案就是给instance的声明加上volatile关键字即可。于是就出现了下面第7总写法。

  7:Double check(volatile)

/**
 * Double check volatile
 * Created by gan on 2019/11/17 18:03.
 */
public class DoubleCheckSingleton {
  private volatile static DoubleCheckSingleton instance;

  private DoubleCheckSingleton() {}

  public static DoubleCheckSingleton getInstance() {
    if (instance == null) {
      synchronized (DoubleCheckSingleton.class) {
        if (instance == null) {
          instance = new DoubleCheckSingleton();
        }
      }
    }
    return instance;
  }
}

volatile关键字的其中一个作用就是禁止指令重排序,把instance声明volatile后,对它的操作就会有一个内存屏障(什么是内存屏障?),这样在赋值完成之前,就不会调用读操作。这里具体的原因网上也是众说纷纭,这里不进行具体阐述。

  8:ThreadLocal实现单例模式(线程安全)

/**
 * ThreadLocal实现单利模式
 * Created by gan on 2019/11/17 18:17.
 */
public class ThreadLocalSingleton {

  private static final ThreadLocal threadLocal = new ThreadLocal() {
    @Override
    protected ThreadLocalSingleton initialValue() {
      return new ThreadLocalSingleton();
    }
  };

  private ThreadLocalSingleton(){}

  public static ThreadLocalSingleton getInstance(){
    return threadLocal.get();
  }
}

  ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程堆数据的访问冲突。对于多线程资源共享问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal则采用了“以空间换时间”的方式(主要就是避免了加锁排队)。 前者提供一份变量,让不同的线程排队访问,而后者为每一个线程提供了一份变量,因此可以同时访问而互不影响。但是实际是创建了多个单例对象的。

单例模式的破坏:

  1:序列化破坏

    一个对象创建好以后,有时候需要将对象序列化然后写入磁盘。下次在从磁盘中读取并反序列化,将其转化为内存对象。反序列化后的对象会重新分配内存,即创建型的对象。这样就违背了单例模式的初衷。解决这种方式的方法就是在单例类中新增一个 private Object readResolve();方法即可,具体原因可以看看序列化和反序列化的源码。

  2:反射

    通过反射“暴力破解”也能破坏单例模式,具体暂时不阐述。

  3:克隆

    克隆也会破坏单例模式,具体暂时不阐述。

代码链接:https://gitee.com/ganganbobo/gps-parent

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文基于https://major.io/2014/05/13/coreos-vs-project-atomic-a-review/的内容,对CoreOS和Atomic两个操作系统进行了详细的对比,涵盖部署、管理和安全性等多个方面。 ... [详细]
  • 深入探讨配置文件的管理与优化
    尽管配置文件的重要性不言而喻,但其管理和安全性问题却常被忽视。本文将详细讨论配置文件的不同管理策略及其优缺点。 ... [详细]
  • databasesync适配openGauss使用指导书
    一、database-sync简介database-sync作为一种开源辅助工具,用于数据库之间的表同步,更确切的说法是复制,可以从一个数据库复制表到另一个数据库该工具支持的功能如 ... [详细]
  • APP数据包捕获挑战
    本文探讨了在使用Burp Suite捕获移动应用数据包时遇到的两大难题,尤其是SSL Pinning安全机制的影响,并提供了一种解决方案。 ... [详细]
  • 使用Docker部署Gitea自托管Git服务
    Gitea是由Gogs社区分叉而来的开源自托管Git服务,旨在提供一个更加灵活和易于维护的解决方案。本文将详细介绍如何利用Docker容器技术快速部署Gitea。 ... [详细]
  • Flutter 高德地图插件使用指南
    本文档详细介绍了如何在Flutter项目中集成和使用高德地图插件,包括安装、配置及基本使用方法。 ... [详细]
  • Iris 开发环境配置指南 (最新 Go & IntelliJ IDEA & Iris V12)
    本指南详细介绍了如何在最新的 Go 语言环境及 IntelliJ IDEA 中配置 Iris V12 框架,适合初学者和有经验的开发者。文章提供了详细的步骤说明和示例代码,帮助读者快速搭建开发环境。 ... [详细]
  • WorldWind源代码解析:瓦片调度机制详解
    本文深入探讨了WorldWind项目中的关键组件——瓦片调度策略。通过源代码分析,我们将了解摄像头移动时如何动态调整瓦片的加载与卸载,确保地图渲染的高效与流畅。 ... [详细]
  • scrapyredis分布式爬虫 ... [详细]
  • 本文将探讨iOS开发过程中需要掌握的三种关键编程语言——C、Objective-C和Swift,并深入解析面向过程与面向对象编程的概念,同时对比iOS与Android两大移动平台的特点。 ... [详细]
  • 本文档详细介绍了在Vue2项目中集成Vant v2组件库的方法,包括安装步骤、版本选择以及如何参考官方文档进行开发。同时提供了其他常用Vue组件库的链接,供开发者对比选择。 ... [详细]
  • 使用H5在前端生成Excel文件的方法
    本文介绍了一种利用HTML5和JavaScript库在浏览器端直接生成并下载Excel文件的技术方案。通过引入alasql.js和xlsx.core.min.js两个库,可以轻松实现数据导出功能。 ... [详细]
  • 十大排序算法JavaScript实现总结
    十大排序算法JavaScript实现总结,Go语言社区,Golang程序员人脉社 ... [详细]
  • Linux 5.3内核正式发布并标记为稳定
    知名Linux内核开发者Greg Kroah-Hartman宣布,最新的Linux 5.3内核已正式标记为稳定版本,适用于大规模部署。该版本带来了多项新特性和改进,包括对最新硬件的支持和性能优化。 ... [详细]
  • 在上一期文章中,我们探讨了FastDev4Android项目中PullToRefreshListView组件的使用方法。本期将继续探讨该框架中的另一个重要组件——ACache数据缓存器,详细介绍其工作原理及如何在项目中有效利用。 ... [详细]
author-avatar
xi曦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有