【编程进阶知识】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世界的一把钥匙。如果你有更多的见解或者遇到了难题,不妨在评论区分享,让我们一起探讨,共同进步!

目录
相关文章
|
4天前
|
设计模式 安全 Java
Java编程中的单例模式:理解与实践
【10月更文挑战第31天】在Java的世界里,单例模式是一种优雅的解决方案,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的实现方式、使用场景及其优缺点,同时提供代码示例以加深理解。无论你是Java新手还是有经验的开发者,掌握单例模式都将是你技能库中的宝贵财富。
12 2
|
6天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
4天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
4天前
|
存储 分布式计算 Java
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
17 2
|
4天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
|
3天前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
6 0
|
28天前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
63 0
|
28天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
50 0
|
28天前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
58 0
|
28天前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
74 0

推荐镜像

更多