美文网首页
设计模式【1.3】-- 为什么饿汉式单例是线程安全的?

设计模式【1.3】-- 为什么饿汉式单例是线程安全的?

作者: 秦怀杂货店 | 来源:发表于2021-01-31 11:30 被阅读0次

我们都知道,饿汉式单例是线程安全的,也就是不会初始化的时候创建出两个对象来,但是为什么呢?

首先定义一个饿汉式单例如下:

 public class Singleton {
    // 私有化构造方法,以防止外界使用该构造方法创建新的实例
    private Singleton(){
    }
    // 默认是public,访问可以直接通过Singleton.instance来访问
    static Singleton instance = new Singleton();
}

之所以是线程安全的,是因为JVM在类加载的过程,保证了不会初始化多个static对象。类的生命周期主要是:

加载-->验证-->准备-->解析-->初始化-->使用-->卸载

上面的代码,实际上类成员变量instance是在初始化阶段的时候完成初始化,所有的类变量以及static静态代码块,都是在一个叫clinit()的方法里面完成初始化。这一点,使用jclasslib可以看出来:

image

clinit()方法是由虚拟机收集的,包含了static变量的赋值操作以及static代码块,所以我们代码中的static Singleton instance = new Singleton();就是在其中。虚拟机本身会保证clinit()代码在多线程并发的时候,只会有一个线程可以访问到,其他的线程都需要等待,并且等到执行的线程结束后才可以接着执行,但是它们不会再进入clinit()方法,所以是线程安全的。我们可以验证一下:

首先改造一下单例:

public class Singleton {
    // 私有化构造方法,以防止外界使用该构造方法创建新的实例
    private Singleton() {
    }

    // 默认是public,访问可以直接通过Singleton.instance来访问
    static Singleton instance = null;

    static {
        System.out.println("初始化static模块---开始");
        instance = new Singleton();
        try {
            System.out.println("初始化中...");
            Thread.sleep(20 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("初始化static模块----结束");
    }
}

测试代码:

import java.io.*;
import java.lang.reflect.InvocationTargetException;

public class SingletonTests {
    public static void main(String[] args) throws Exception, InvocationTargetException, InstantiationException, NoSuchMethodException {
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("线程1开始尝试初始化单例");
                Singleton singleton = Singleton.instance;
                System.out.println("线程1获取到的单例:" + singleton);
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                System.out.println("线程2开始尝试初始化单例");
                Singleton singleton = Singleton.instance;
                System.out.println("线程2获取到的单例:" + singleton);
            }
        });
        thread1.start();
        thread2.start();
    }
}

运行结果,一开始运行的时候,我们可以看到线程1进去了static代码块,它在初始化,线程2则在等待。

image-20201217141915904

待到线程1初始化完成的时候,线程2也不会再进入static代码块,而是和线程1取得同一个对象,由此可见,static代码块实际上就是线程安全的。

image-20201217143603156

相关文章

  • 单例(Singleton)

    单例设计模式 单例模式的创建 饿汉式1 饿汉式2 懒汉式 优化后的懒汉式(线程安全): 饿汉式与懒汉式区别 单例模...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • Singleton 单例模式

    饿汉式单例模式 饿汉式单例模式 通过静态代码块增加异常处理 懒汉式单例模式 存在线程安全问题 懒汉式单例模式 解决...

  • Java设计模式一--单例模式

    一、单例模式单例模式主要分为饿汉式、懒汉式(非线程安全、线程安全、双重检查)、静态内部类、枚举。1.饿汉式 2.懒...

  • 05.单例模式(创建型)

    创建型模式-单例模式 一、饿汉式单利模式 饿汉式不需要考虑线程安全问题。 饿汉式比较浪费资源 二、懒汉式单例模式 ...

  • 单例模式

    单例模式八种设计模式说明饿汉式(线程安全)代码public class Hungry {private stati...

  • 关于单例模式的思考

    饿汉式单例模式(写法一) /** * 饿汉式单例 写法一 * 优点:简单,性能高,线程安全 * 缺点:浪费内存(当...

  • Kotlin 的单例模式

    Kotlin 的单例模式(5种) Kotlin 的5种单例模式: 饿汉式 懒汉式 线程安全的懒汉式 双重校验锁式 ...

  • 源码 : 设计模式之单例模式的5种实现

    RelaxHeart网-王琦:源码 : 设计模式之单例模式的5种实现 方式一:饿汉式 方式二:懒汉式 线程安全问题...

  • Java 单例模式

    概述 Java中单例模式是一种常见的设计模式,单例模式总共有7种写法。 懒汉,线程不安全 懒汉,线程安全 饿汉 饿...

网友评论

      本文标题:设计模式【1.3】-- 为什么饿汉式单例是线程安全的?

      本文链接:https://www.haomeiwen.com/subject/xstrtltx.html