设计模式之单例

文章目录


前言

单例模式是比较经典的设计模式,本篇我们将结合实际案例说明单例模式的实现原理,以及存在的问题。


一、单例模式的基本定义

单例模式(Singleton Pattern)确保某一个类只有一个实例方法,而且自行实例化并向整个系统提供这个实例,这个类被称为单例类,它提供全局访问方法。单例模式的要点有三个:

  • 某个类只能有一个实例
  • 它必须自行创建这个实例
  • 它必须自行向整个系统提供这个实例

二、懒汉式单例

代码实现如下:

java 复制代码
public class LazyWaterCup {
    // 定义一个静态引用
    private static LazyWaterCup wc;

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

    // 提供一个全局访问点
    public static LazyWaterCup getInstance() {
        if (wc == null){
        	wc = new LazyWaterCup();
        }
        return wc;
    }
}

首先我们要声明一个类引用,其次构造方法必须是私有的,以保证外部对于此类无法直接通过new进行类的实例化,与此同时我们要提供一个对外的方法,在此方法中实现类的实例化,也需要进行一个判断,若类引用没有指向任何对象,就创建一个对象,让引用指向它。

测试结果如下:

java 复制代码
public class Test {
    public static void main(String[] args) {
        LazyWaterCup lwc1 = LazyWaterCup.getInstance();
        LazyWaterCup lwc2 = LazyWaterCup.getInstance();
        LazyWaterCup lwc3 = LazyWaterCup.getInstance();
        System.out.println("lwc1= " + lwc1);
        System.out.println("lwc2= " + lwc2);
        System.out.println("lwc3= " + lwc3);
    }
}

如图三次调用都指向同一个对象,因为从第一个getInstance()被调用时,由于类引用指向->null所以会创建对象,后续再调用getInstance()方法就不会再创建对象而是将上一个对象地址返回。因此三次打印结果都是指向同一个对象。

三、饿汉式单例

代码如下:

java 复制代码
public class HungryWaterCup {
    // 定义一个静态引用
    private final static HungryWaterCup wc = new HungryWaterCup();
    
    // 构造方法私有
    private HungryWaterCup(){}

    // 提供一个全局访问点
    public static HungryWaterCup getInstance() {
        return wc;
    }
}

饿汉式单例与懒汉式单例的区别在于它对于对象的创建比较急迫,在声明的时候就创建一个对象,使声明的引用指向它,它是在类的装载过程中就完成的。

测试结果如下:

java 复制代码
public class Test {
    public static void main(String[] args) {
        HungryWaterCup hwc1 = HungryWaterCup.getInstance();
        HungryWaterCup hwc2 = HungryWaterCup.getInstance();
        HungryWaterCup hwc3 = HungryWaterCup.getInstance();
        System.out.println("hwc1= " + hwc1);
        System.out.println("hwc2= " + hwc2);
        System.out.println("hwc3= " + hwc3);
    }
}

四、懒汉式单例存在的线程安全问题

懒汉式单例是在getInstance()被调用的时候创建的,若此时有大量线程访问此方法,就会创建多个对象,我们可以测试一下:

在创建对象的时候先让线程睡眠1ms,方便模拟多线程环境。

java 复制代码
public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 200; i++) {
            Thread t1 = new Thread(() -> {
                try {
                    LazyWaterCup lwc = LazyWaterCup.getInstance();
                    System.out.println("lwc->" + lwc);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }, "t1");
            t1.start();
        }
    }
}

在测试类代码中,创建200个线程。运行结果如下图:

创建的对象很多不一样。解决线程安全问题,一般可以在可能出现并发安全的代码块加上对象锁,就是synchronized锁,我们上锁之后,再试试:

测试结果:

就可以保证创建的对象是同一个。


总结

今天对单例模式的分析就到这里了,后续会继续总结其他设计模式的原理与实现。

相关推荐
子非吾喵2 分钟前
IDEA在设置里边更改了JDK版本没生效问题解决
java·ide·intellij-idea
陆业聪7 分钟前
基本数据类型:Kotlin、Dart (Flutter)、Java 和 C++ 的比较
java·flutter·kotlin
程序修理员8 分钟前
2个word内容合并
java·word
醇氧8 分钟前
【idea】更换快捷键
java·ide·intellij-idea
容器( ु⁎ᴗ_ᴗ⁎)ु.。oO22 分钟前
java中的定时器
java·开发语言
薯条不要番茄酱1 小时前
数据结构-5.二叉树
java·开发语言·数据结构
YAy172 小时前
CC3学习记录
java·开发语言·学习·网络安全·安全威胁分析
代码小鑫2 小时前
A035-基于Spring Boot的企业内管信息化系统
java·开发语言·spring boot·后端·spring
qq_35323353893 小时前
【原创】java+ssm+mysql校园疫情防控管理系统设计与实现
java·mvc·javaweb·ssm框架·bs·疫情防控
代码调试6 小时前
Springboot校园失物招领平台
java·spring boot