【编程进阶知识】Java单例模式深度解析:饿汉式与懒汉式实现技巧

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 本文深入解析了Java单例模式中的饿汉式和懒汉式实现方法,包括它们的特点、实现代码和适用场景。通过静态常量、枚举类、静态代码块等方式实现饿汉式,通过非线程安全、同步方法、同步代码块、双重检查锁定和静态内部类等方式实现懒汉式。文章还对比了各种实现方式的优缺点,帮助读者在实际项目中做出更好的设计决策。

Java单例模式深度解析:饿汉式与懒汉式实现技巧

摘要: 在Java编程中,单例模式是一种常用的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。本文深入探讨了饿汉式和懒汉式单例模式的实现方法,包括它们的特点、实现代码和适用场景。通过阅读本文,你将能够理解单例模式的核心要点,并掌握如何在实际项目中正确应用这两种单例模式。

关键词: Java单例模式,饿汉式,懒汉式,设计模式,线程安全

1. 单例模式概述

单例模式是一种创建型设计模式,它确保某个类只有一个实例,并提供一个全局访问点。这种模式在Java中广泛应用,如Runtime类。

核心要点:

  1. 唯一实例: 类的实例必须是唯一的。
  2. 内部创建: 构造器必须私有化,防止外部创建实例。
  3. 全局访问: 提供一个公共的静态方法获取实例。
  4. 创建时机: 分为饿汉式和懒汉式。

2. 饿汉式单例模式

饿汉式单例模式在类加载时就创建实例,避免了线程同步问题。

2.1 实现方式

  1. 静态常量实例化
  2. 枚举类
  3. 静态代码块

2.2 代码示例

//1、静态常量直接实例化(代表jvm运行环境的Runtime类生成单例,采用的就是这种实现方式,简洁直观);
class HungrySigleton1 {
   
    private HungrySigleton1(){
   
    }
    private final static HungrySigleton1 INSTANCE = new HungrySigleton1();
    public static HungrySigleton1 getInstance(){
   
        return INSTANCE;
    }
}

//2、枚举类(最简洁);
enum HungrySigleton3{
   
    INSTANCE;
    private HungrySigleton3() {
   
    }
}

//3、静态代码块(适合复杂的场景,如从配置文件读取所需参数)。
class HungrySigleton2 {
   
    private HungrySigleton2(String info){
   System.out.println(info);}
    private  static HungrySigleton2 INSTANCE =null;
    static {
   
        Properties prop = new Properties();
        InputStream inStream = HungrySigleton2.class.getClassLoader().getResourceAsStream("project.properties");
        try {
   prop.load(inStream);} catch (IOException e) {
   e.printStackTrace();}
        String info =(String) prop.get("info");
        //扩展:读取配置文件的几种方式 1、Properties继承自Hashtable 2、ResourceBundle 3、Spring种利用ApplicationContext加载xml文件
         INSTANCE = new HungrySigleton2(info);
    }
    public static HungrySigleton2 getInstance(){
   
        return INSTANCE;
    }
}
public class Hungry{
   
    public static void main(String[] args) {
   
        HungrySigleton1 ins1 = HungrySigleton1.getInstance();
        HungrySigleton2 ins2 = HungrySigleton2.getInstance();
        HungrySigleton3 ins3 = HungrySigleton3.INSTANCE;
    }
}

3. 懒汉式单例模式

懒汉式单例模式在第一次使用时才创建实例,可能需要处理线程同步问题。

3.1 实现方式

  1. 非线程安全版本
  2. 同步方法实现
  3. 同步代码块实现
  4. 双重检查锁定(DCL)
  5. 静态内部类

3.2 代码示例

class LazySigleton1 {
   
    private LazySigleton1(){
   
    }
    private  static LazySigleton1 INSTANCE =null;
    public static LazySigleton1 getInstance(){
   
        if(null == INSTANCE){
   
            INSTANCE = new LazySigleton1();
        }
        return INSTANCE;
    }
}

//2、懒汉式版本2(同步方法实现线程安全,但效率很低);
class LazySigleton2 {
   
    private LazySigleton2(){
   
    }
    private  static LazySigleton2 INSTANCE =null;
    public synchronized static LazySigleton2 getInstance(){
   
        if(null == INSTANCE){
   
            try {
   Thread.sleep(100);} catch (InterruptedException e) {
   e.printStackTrace();}
            INSTANCE = new LazySigleton2();
        }
        return INSTANCE;
    }
}

//3、懒汉式版本2(同步代码块提供了一定效率,但存在线程安全问题);
class LazySigleton3 {
   
    private LazySigleton3(){
   
    }
    private  static LazySigleton3 INSTANCE =null;
    public  static LazySigleton3 getInstance(){
   
        if(null == INSTANCE){
   
            try {
   Thread.sleep(100);} catch (InterruptedException e) {
   e.printStackTrace();}
            synchronized(INSTANCE){
   
                INSTANCE = new LazySigleton3();
            }
        }
        return INSTANCE;
    }
}

//4、懒汉式版本2(DCL(Double Check Lock),线程安全效率较高,但由于synchronized无法避免jvm优化时可能出现的乱序执行,可能会遇到拿到的实例对象尚未被正确初始化的问题,这时可以利用volatile来解决);
class LazySigleton4 {
   
    private LazySigleton4(){
   
    }
    private  static volatile LazySigleton4 INSTANCE =null;
    public  static LazySigleton4 getInstance(){
   
        if(null == INSTANCE){
   
            try {
   Thread.sleep(100);} catch (InterruptedException e) {
   e.printStackTrace();}
            synchronized(INSTANCE){
   
                if(null == INSTANCE){
   
                    INSTANCE = new LazySigleton4();
                }
            }
        }
        return INSTANCE;
    }
}
//5、静态内部类(内部类被加载和初始化时才会创建对象,即调用时)
class LazySigleton5 {
   
    private LazySigleton5(){
   
    }
    private static class Inner{
   
         private final static LazySigleton5 INSTANCE = new LazySigleton5();
    }
    public static LazySigleton5 getInstance(){
   
        return Inner.INSTANCE;
    }
}

public class Lazy {
   
    public static void main(String[] args) throws InterruptedException, ExecutionException {
   
        ExecutorService exec = Executors.newFixedThreadPool(2);
        Callable task = new Callable(){
   
            @Override
            public Object call() {
   
                LazySigleton1 instance = LazySigleton1.getInstance();
                System.out.println(Thread.currentThread().getName()+instance);
                return instance;
            }
        };
        Future f1 = exec.submit(task);
        Future f2 = exec.submit(task);
        LazySigleton1 l1 = (LazySigleton1) f1.get(),l2 = (LazySigleton1) f2.get();
        System.out.println("l1:" + l1);
        System.out.println("l2:" + l2);
        System.out.println(l1==l2);
        exec.shutdown();
    }
}

4. 优缺点对比

实现方式 优点 缺点
饿汉式 - 线程安全
- 简单直观
- 类加载时就创建实例,可能导致资源浪费
懒汉式非线程安全 - 延迟实例化
- 节省资源
- 非线程安全
懒汉式同步方法 - 线程安全 - 效率低,每次访问都要同步
懒汉式同步代码块 - 线程安全
- 效率较高
- 复杂的线程同步问题
双重检查锁定 - 线程安全
- 高效
- 需要处理指令重排问题
静态内部类 - 线程安全
- 延迟加载
- 代码复杂度较高

5. 流程图

graph TD
    A[开始] --> B[检查实例]
    B -->|不存在| C[创建实例]
    B -->|存在| D[返回实例]
    C --> D

6. 结语

单例模式是Java中一种非常重要的设计模式,适用于需要严格控制实例数量的场景。通过本文的介绍,你应该对饿汉式和懒汉式单例模式有了更深入的理解。希望这些信息能帮助你在实际开发中做出更好的设计决策。

思维导图:

graph LR
    A[单例模式] --> B[饿汉式]
    A --> C[懒汉式]
    B --> D[静态常量]
    B --> E[枚举类]
    B --> F[静态代码块]
    C --> G[非线程安全]
    C --> H[同步方法]
    C --> I[同步代码块]
    C --> J[双重检查锁定]
    C --> K[静态内部类]

Excel表格:

实现方式 优点 缺点
饿汉式 - 线程安全
- 简单直观
- 类加载时就创建实例,可能导致资源浪费
懒汉式非线程安全 - 延迟实例化
- 节省资源
- 非线程安全
懒汉式同步方法 - 线程安全 - 效率低,每次访问都要同步
懒汉式同步代码块 - 线程安全
- 效率较高
- 复杂的线程同步问题
双重检查锁定 - 线程安全
- 高效
- 需要处理指令重排问题
静态内部类 - 线程安全
- 延迟加载
- 代码复杂度较高

鼓励话语: 掌握单例模式,就像是掌握了控制Java世界的一把钥匙。如果你有更多的见解或者遇到了难题,不妨在评论区分享,让我们一起探讨,共同进步!

目录
相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
14 2
|
6天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
49 6
|
6天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
8天前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
90 53
|
4天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
13 4
|
7天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
4天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
6天前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
16 2
|
7天前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####
|
7天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。

推荐镜像

更多