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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 本文深入解析了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;
    }
}
AI 代码解读

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();
    }
}
AI 代码解读

4. 优缺点对比

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

5. 流程图

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

6. 结语

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

思维导图:

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

Excel表格:

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

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

目录
打赏
0
0
0
0
87
分享
相关文章
Java中Log级别和解析
日志级别定义了日志信息的重要程度,从低到高依次为:TRACE(详细调试)、DEBUG(开发调试)、INFO(一般信息)、WARN(潜在问题)、ERROR(错误信息)和FATAL(严重错误)。开发人员可根据需要设置不同的日志级别,以控制日志输出量,避免影响性能或干扰问题排查。日志框架如Log4j 2由Logger、Appender和Layout组成,通过配置文件指定日志级别、输出目标和格式。
Java二维数组的使用技巧与实例解析
本文详细介绍了Java中二维数组的使用方法
54 15
深潜数据海洋:Java文件读写全面解析与实战指南
通过本文的详细解析与实战示例,您可以系统地掌握Java中各种文件读写操作,从基本的读写到高效的NIO操作,再到文件复制、移动和删除。希望这些内容能够帮助您在实际项目中处理文件数据,提高开发效率和代码质量。
19 0
【潜意识Java】深度解析黑马项目《苍穹外卖》与蓝桥杯算法的结合问题
本文探讨了如何将算法学习与实际项目相结合,以提升编程竞赛中的解题能力。通过《苍穹外卖》项目,介绍了订单配送路径规划(基于动态规划解决旅行商问题)和商品推荐系统(基于贪心算法)。这些实例不仅展示了算法在实际业务中的应用,还帮助读者更好地准备蓝桥杯等编程竞赛。结合具体代码实现和解析,文章详细说明了如何运用算法优化项目功能,提高解决问题的能力。
76 6
【潜意识Java】期末考试可能考的高质量大题及答案解析
Java 期末考试大题整理:设计一个学生信息管理系统,涵盖面向对象编程、集合类、文件操作、异常处理和多线程等知识点。系统功能包括添加、查询、删除、显示所有学生信息、按成绩排序及文件存储。通过本题,考生可以巩固 Java 基础知识并掌握综合应用技能。代码解析详细,适合复习备考。
28 4
|
2月前
|
【潜意识Java】期末考试可能考的选择题(附带答案解析)
本文整理了 Java 期末考试中常见的选择题,涵盖数据类型、控制结构、面向对象编程、集合框架、异常处理、方法、流程控制和字符串等知识点。每道题目附有详细解析,帮助考生巩固基础,加深理解。通过这些练习,考生可以更好地准备考试,掌握 Java 的核心概念和语法。
41 1
【潜意识Java】期末考试可能考的简答题及答案解析
为了帮助同学们更好地准备 Java 期末考试,本文列举了一些常见的简答题,并附上详细的答案解析。内容包括类与对象的区别、多态的实现、异常处理、接口与抽象类的区别以及垃圾回收机制。通过这些题目,同学们可以深入理解 Java 的核心概念,从而在考试中更加得心应手。每道题都配有代码示例和详细解释,帮助大家巩固知识点。希望这些内容能助力大家顺利通过考试!
28 0
|
7天前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
115 60
【Java并发】【线程池】带你从0-1入门线程池
|
3天前
|
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
38 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
81 14

热门文章

最新文章

推荐镜像

更多
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等