Java设计模式之单例设计模式【精选】

本文介绍了Java中的单例模式,包括饿汉式和懒汉式的实现,讨论了它们的特点、优缺点以及线程安全问题。饿汉式在类加载时即创建实例,适合经常使用的场景;懒汉式则在首次使用时创建,适用于偶发使用的情况。文章还提供了多线程环境下验证单例创建的示例代码。
摘要由CSDN通过智能技术生成

点击 Mr.绵羊的知识星球 解锁更多优质文章。

目录

一、介绍

1. 简介

2. 特点

3. 优点

4. 缺点

二、使用

1. 饿汉式

2. 懒汉式

3. 验证多线程创建单实例

4. 饿汉式和懒汉式应用场景


一、介绍

1. 简介

    一个类只能实例化一个对象。

2. 特点

    (1) 在Java程序中,一个类有且仅有一个实例化的对象。

    (2) 单例类必须自己创建自己的唯一实例。

    (3) 单例类必须给所有其他对象提供这一实例。

3. 优点

    (1)减少了内存的占用。

    (2) 优化对共享资源的访问,避免对共享资源的多重占用。

    (3) 避免了频繁创建和回收实例的消耗。

4. 缺点

    (1) 单例类的扩展有很大的困难。

    (2) 成员变量线程不安全。

    如果真的有必要使用单例模式,共享变量一般都是通过加锁或使用JUC中的类来解决线程安全问题。

二、使用

    本文主要围绕单例模式中的饿汉式和懒汉式进行使用描述。

1. 饿汉式

    类被加载的时候就创建了该实例。(不了解类的加载可以看这篇文章:类的加载)

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 单例设计模式-饿汉式
 *
 * @author wxy
 * @date 2023-03-02
 */
public class StarvedManMode {
    /**
     * 类加载时会自动加载常量或成员变量, 所以随着类的加载, 会创建该实例。
     */
    private static final StarvedManMode STARVED_MAN_MODE = new StarvedManMode();

    /**
     * 由于全局仅有一个实例, 避免操作共享变量出现线程安全问题, 所以使用原子类。
     */
    private AtomicInteger count;

    private StarvedManMode() {
        // 必须私有化构造,避免外部直接创建实例(不考虑反射创建对象实例)
    }

    /**
     * 获取实例
     *
     * @return StarvedManMode实例
     */
    public static StarvedManMode getInstance() {
        return STARVED_MAN_MODE;
    }
}

2. 懒汉式

    使用时才创建该实例。(该创建方式使用了volatile关键字,不了解可以看这篇文章:volatile详解

import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 单例设计模式-懒汉式
 *
 * @author wxy
 * @date 2023-03-02
 */
public class LazyManMode {
    /**
     * 需要使用volatile修饰, 避免指令重排。
     */
    private static volatile LazyManMode lazyManMode = null;

    /**
     * 由于全局仅有一个实例, 避免操作共享变量出现线程安全问题, 所以使用原子类。
     */
    private AtomicInteger count;

    private LazyManMode() {
        // 必须私有化构造,避免外部直接创建实例(不考虑反射创建对象实例)
    }

    /**
     * 获取实例
     *
     * @return LazyManMode实例
     */
    public static LazyManMode getInstance() {
        // DCL双端检锁, 如果判断少一层或者不加锁, 多线程下创建对象可能不是单例的
        if (Objects.isNull(lazyManMode)) {
            synchronized (LazyManMode.class) {
                if (Objects.isNull(lazyManMode)) {
                    lazyManMode = new LazyManMode();
                }
            }
        }

        return lazyManMode;
    }
}

3. 验证多线程创建单实例

/**
 * 单例设计模式-懒汉式
 *
 * @author wxy
 * @date 2023-03-02
 */
public class DesignPatternTest {
    public static void main(String[] args) {
        for (int count = 0; count < 5; count++) {
            new Thread(() -> {
                for (int index = 0; index < 5; index++) {
                    // 多线程中创建并获取懒汉式实例
                    LazyManMode lazyManModeInstance = LazyManMode.getInstance();
                    /*
                    不重写toString方法, 打印实例地址, 打印结果:
                    懒汉式实例地址: com.***.LazyManMode@4e9c993a
                    懒汉式实例地址: com.***.LazyManMode@4e9c993a
                    ...
                     */
                    System.out.println("懒汉式实例地址: " + lazyManModeInstance);

                    /*-----------------分割线------------------*/

                    // 多线程中创建并获取饿汉式实例
                    StarvedManMode starvedManModeInstance = StarvedManMode.getInstance();
                    /*
                    不重写toString方法, 打印实例地址, 打印结果:
                    饿汉式实例地址: com.***.StarvedManMode@405b7e99
                    饿汉式实例地址: com.***.StarvedManMode@405b7e99
                    ...
                     */
                    System.out.println("饿汉式实例地址: " + starvedManModeInstance);
                }
            }).start();
        }
    }
}

4. 饿汉式和懒汉式应用场景

    当系统对内存使用要求比较高时或者实例中没有共享变量可以使用单例模式,如果实例中有共享变量,使用时要保证其线程安全。

    (1) 如果这个实例和实例中方法经常被使用,建议用饿汉式。

    (2) 如果这个实例和实例中的方法就是为了偶然突发状况用那么一次,建议使用懒汉式。

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒阳快跑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值