IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 单例模式的简单实现 -> 正文阅读

[Java知识库]单例模式的简单实现

什么是单例模式?
概念:23种设计模式之一,通过单例模式的方法创建的类在整个应用程序中只有一个实例。
核心思想:构造方法私有化
实现方式
单利模式的实现方式有很多,在此简单介绍以下几种写法

  1. 饿汉式
  2. 懒汉式
  3. 双重检测锁模式
  4. 静态内部类
  5. 枚举类

代码示例
饿汉式:类加载的时候就创建该类的唯一实例对象,天生线程安全。无论该对象是否被使用,在类加载的时候都会被创建,一定程度上导致内存资源的浪费。

//饿汉式单例
public class Hungry {
    private Hungry() {
    }

    private static final Hungry HUNGRY_SINGLETON = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY_SINGLETON;
    }
}

懒汉式:实例对象被使用时才会被创建,实现了内存资源的合理分配。但多线程并发下会破坏这种单例,即线程不安全

//懒汉式单例
public class Lazy {
    private Lazy() {
    }

    private static Lazy lazySingleton = null;

    public static Lazy getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new Lazy();
        }
        return lazySingleton;
    }
}

为什么上述懒汉式单例是线程不安全的呢,如下代码,在Lazy类的无参构造中加入一行打印并引入多线程场景进行测试

//懒汉式单例
public class Lazy {
    private Lazy() {
        System.out.println("线程:"+Thread.currentThread().getName()+"创建了对象");
    }

    private static Lazy lazySingleton = null;

    public static Lazy getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new Lazy();
        }
        return lazySingleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                Lazy instance = lazySingleton.getInstance();
                System.out.println(instance);
            }).start();
        }
    }
}

运行结果:有5个线程执行了Lazy构造方法创建了5个实例对象(就本次运行结果而言),不符合单例模式的设计。那怎样解决这个问题,实现既能合理的分配内存资源又可以保证线程安全呢?于是就有了下面我们要讲的 双重检测锁模式

D:\installPath\Java\jdk1.8.0_121\bin\java.exe "-javaagent:D:\installPath\IntelliJ IDEA 
线程:Thread-0创建了对象
线程:Thread-4创建了对象
线程:Thread-3创建了对象
线程:Thread-1创建了对象
线程:Thread-2创建了对象
com.kang.singleton.Lazy@34a705cd
com.kang.singleton.Lazy@34a705cd
com.kang.singleton.Lazy@56d4b179
com.kang.singleton.Lazy@56d4b179
com.kang.singleton.Lazy@15c05ae4
com.kang.singleton.Lazy@45b30948
com.kang.singleton.Lazy@45b30948
com.kang.singleton.Lazy@14f482d7
com.kang.singleton.Lazy@14f482d7
com.kang.singleton.Lazy@14f482d7

Process finished with exit code 0

双重检测锁模式: 也就是我们所说的DCL懒汉式

//双重检测锁模式
public class DCL {
    private DCL() {
    }
    
    //因为 new DCL();不是一个原子操作,这里使用volatile修饰dclSingleton 防止指令重排
    private volatile static DCL dclSingleton = null;

    public static DCL getInstance() {
        if (dclSingleton == null) {
            synchronized (Lazy.class) {
                if (dclSingleton == null) {
                    dclSingleton = new DCL();//不是一个原子操作
                }
            }
        }
        return dclSingleton;
    }
}

引入同样的多线程测试场景进行测试

//双重检测锁模式
public class DCL {
    private DCL() {
        System.out.println("线程:" + Thread.currentThread().getName() + "创建了对象");
    }

    //因为 new DCL();不是一个原子操作,这里使用volatile修饰dclSingleton 防止指令重排
    private volatile static DCL dclSingleton = null;

    public static DCL getInstance() {
        if (dclSingleton == null) {
            synchronized (Lazy.class) {
                if (dclSingleton == null) {
                    dclSingleton = new DCL();//不是一个原子操作
                }
            }
        }
        return dclSingleton;
    }
    
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                DCL instance = dclSingleton.getInstance();
                System.out.println(instance);
            }).start();
        }
    }
}

运行结果:程序执行只创建了一个实例,符合预期结果(此处暂不考虑Java反射技术对单例模式的影响)

D:\installPath\Java\jdk1.8.0_121\bin\java.exe "-javaagent:D:\installPath\IntelliJ IDEA 
线程:Thread-0创建了对象
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec
com.kang.singleton.Lazy@6b047ec

Process finished with exit code 0

静态内部类: 在类的一个生命周期中静态代码块只在类加载时执行一次,可以满足单例模式的设计要求(此处暂不考虑Java反射技术对单例模式的影响)

//静态内部类
public class Singleton {
    private Singleton() {
    }
	//提供一个公开的方法作为访问该唯一实例的入口
    public static Singleton getInstance(){
        return Test.SINGLETON;
    }
    
    //创建静态内部类Test
    public static class Test {
        private static final Singleton SINGLETON = new Singleton();
    }
}

枚举类: 枚举类型是Java 5中新增特性的一部分,也是类(class)的一种,自带单例模式,且可以防止Java反射技术对单例模式的破坏

//枚举类(enum)
public enum Enumeration {
    INSTANCE;

    public static Enumeration getInstance(){
        return Enumeration.INSTANCE;
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:35:37  更:2022-03-21 20:35:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:52:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码