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知识库]单例模式(两种)

目录

一,概念

二,应用实例

三,饿汉模式

四,懒汉模式(单线程)

五,懒汉模式(多线程且效率低)

六,懒汉模式(双重校验锁)

七,破坏单例模式

八,总结


一,概念

  • 单例模式是指在内存中只会创建且仅创建一次对象的设计模式
  • 在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象

?

二,应用实例

在jdbc操作时,使用了DataSource(数据库连接池)

三,饿汉模式

  • 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用
public class Singleton {
    //在类加载时就已经创建好,不存在线程安全和并发问题
    private static Singleton instance  = new Singleton();

    private static Singleton getInstance(){
        return instance;
    }
}

四,懒汉模式(单线程)

  • 懒汉式:在真正需要使用对象时才去创建该单例类对象
public class SingletonLazy {
    //类加载时不创建,在调用方法时才实例化一个单例对象
    //线程不安全
    private static SingletonLazy instance = null;

    public static SingletonLazy getInstance(){
        if(instance == null){
            instance = new SingletonLazy();
        }
        return instance;
    }
}

五,懒汉模式(多线程且效率低)

public class SingletonLazy {
    //类加载时不创建,在调用方法时才实例化一个单例对象
    private static SingletonLazy instance = null;

    public static SingletonLazy getInstance(){
        //线程安全了,但是每次获取对象都需要先获取锁,并发性能非常地差
        //极端情况下,可能会出现卡顿现象
        synchronized (SingletonLazy.class) {
            if (instance == null) {
                instance = new SingletonLazy();
            }
        }
        return instance;
    }
}

六,懒汉模式(双重校验锁)

public class SingletonLazy {
    //类加载时不创建,在调用方法时才实例化一个单例对象
    private static volatile SingletonLazy instance = null;

    public static SingletonLazy getInstance(){
        //线程安全了,但是每次获取对象都需要先获取锁,并发性能非常地差
        //极端情况下,可能会出现卡顿现象
        if(instance == null) {
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
}

关于volatile锁的作用:防止指令重排序

创建一个对象,在JVM中会经过三步:

(1)为instance分配内存空间

(2)初始化instance对象

(3)将instance指向分配好的内存空间

指令重排序是指:

  1. JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能
  2. 使用volatile关键字修饰的变量,可以保证其指令执行的顺序与程序指明的顺序一致,不会发生顺序变换
  3. 使用volatile关键字修饰的变量,可以保证其内存可见性,即每一时刻线程读取到该变量的值都是内存中最新的那个值,线程每次操作该变量都需要先读取该变量。

七,破坏单例模式

无论是完美的懒汉式还是饿汉式,终究敌不过反射和序列化,它们俩都可以把单例对象破坏掉(产生多个对象)。

1:利用反射破坏单例模式(利用反射,强制访问类的私有构造器,去创建另一个对象????????)

public static void main(String[] args) {
    // 获取类的显式构造器
    Constructor<Singleton> construct = Singleton.class.getDeclaredConstructor();
    // 可访问私有构造器
    construct.setAccessible(true); 
    // 利用反射构造新对象
    Singleton obj1 = construct.newInstance(); 
    // 通过正常方式获取单例对象
    Singleton obj2 = Singleton.getInstance(); 
    System.out.println(obj1 == obj2); // false
}

2:利用序列化与反序列化破坏单例模式

public static void main(String[] args) {
    // 创建输出流
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Singleton.file"));
    // 将单例对象写到文件中
    oos.writeObject(Singleton.getInstance());
    // 从文件中读取单例对象
    File file = new File("Singleton.file");
    ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
    Singleton newInstance = (Singleton) ois.readObject();
    // 判断是否是同一个对象
    System.out.println(newInstance == Singleton.getInstance()); // false
}

在 JDK1.5 后,使用 Java 语言实现单例模式的方式又多了一种:枚举

public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("这是枚举类型的单例模式!");
    }
}

优势1:代码对比饿汉式与懒汉式来说,更加地简洁

优势2:它不需要做任何额外的操作去保证对象单一性与线程安全性

优势3:使用枚举可以防止调用者使用反射序列化与反序列化机制强制生成多个单例对象,破坏单例模式

八,总结

(1)单例模式常见的写法有两种:懒汉式、饿汉式

(2)懒汉式:在需要用到对象时才实例化对象,正确的实现方式是:双重校验锁,解决了并发安全和性能低下问题

(3)饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。

(4)在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;

(5)如果对内存要求不高使用饿汉式写法,因为简单不易出错,且没有任何并发安全和性能问题

(6)为了防止多线程环境下,因为指令重排序导致变量报异常,需要在单例对象上添加volatile关键字防止指令重排序

(7)最优雅的实现方式是使用枚举,其代码精简,没有线程安全问题,且 Enum 类内部防止反射和反序列化时破坏单例。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:11:18  更:2022-04-01 23:15:37 
 
开发: 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 7:32:23-

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