浅析Java单例设计模式(自写demo)

目录
  • 单例模式特点
  • 单例模式优点
  • 实现方式
    • 饿汉式(线程安全)
    • 懒汉式

单例模式特点

1、构造器私有

2、在一个Java应用程序中,可保证只有一个实例对象

3、只提供一个供外界调用的getInstance()方法

单例模式优点

1、减少某些对象的频繁创建,降低系统开销和内存占用

2、外部调用不使用new关键字,降低系统内存的使用频率

3、对于特殊的类,在系统中只能存在一个实例,否则系统无法正常运行,比如Controller

实现方式

这里简单介绍两种实现方式

饿汉式(线程安全)

/**
 * @author: xuzhilei6656
 * @create: 2021-12-12 12:07
 * @description: 单例模式(饿汉式)
 **/
public class Singleton {
    //创建实例
    private static Singleton instance = new Singleton();

    //私有构造器
    private Singleton(){
    }

    //获取实例的静态方法
    public static Singleton getInstance(){
        return instance;
    }

}

实例对象在类被加载的时候就已经完成初始化,外界调用拿到的都是这个唯一的实例对象

懒汉式

/**
 * @author: xuzhilei6656
 * @create: 2021-12-12 12:22
 * @description: 单例模式(懒汉式)
 **/
public class Singleton {
    //声明一个变量
    private static Singleton instance;

    //私有构造器
    private Singleton(){
    }

    //获取实例的静态方法
    public static Singleton getInstance(){
        //如果是首次调用,实例对象还没有被创建,就需要创建,否则都是返回已经创建过的那个对象
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }

}

对比饿汉式可见,实例对象在类被加载的时候并没有进行创建,在首次调用的时候才被创建,以后再被调用,返回的也是那个唯一的实例对象。

在多线程情况下,这种写法存在线程安全问题,比如:线程A在执行完if判断条件后进入阻塞状态,此时并没有进行对象创建,此时线程B来了,在执行完if条件后直接进行对象创建,等线程A恢复运行状态后也会进行对象创建,这个时候就不符合单例模式了,即出现了线程不安全的问题。

解决方案:在获取实例的静态方法上加synchronized关键字,即加锁

/**
 * @author: xuzhilei6656
 * @create: 2021-12-12 12:22
 * @description: 单例模式(懒汉式)
 **/
public class Singleton {
    //声明一个变量
    private static Singleton instance;

    //私有构造器
    private Singleton(){
    }

    //获取实例的静态方法
    public static synchronized Singleton getInstance(){
        //如果是首次调用,实例对象还没有被创建,就需要创建,否则都是返回已经创建过的那个对象
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }

}

简单粗暴,可达到我们的目的,但是每次获取实例对象都要有加锁操作,影响系统性能。

改进后的方案:双重检查

/**
 * @author: xuzhilei6656
 * @create: 2021-12-12 12:22
 * @description: 单例模式(懒汉式)
 **/
public class Singleton {
    //声明一个变量
    private static Singleton instance;

    //私有构造器
    private Singleton(){
    }

    //获取实例的静态方法
    public static synchronized Singleton getInstance(){
        //第一次检查
        if (instance == null){
            //获取锁
            synchronized (Singleton.class){
                //第二次检查
                if (instance==null){
                    //两次检查都确定没有已存在的实例对象,这才进行对象的创建操作
                    instance = new Singleton();
                }
            }

        }
        return instance;
    }

}

这样不必每次获取实例对象的时候都进行加锁操作,只有在第一次创建对象的时候才进行加锁操作,提高了系统性能。

但是,即使这样有可能会出现。因为 instance = new Singleton()这行代码在JVM中是两个操作,赋值和初始化实例,但JVM并不保证这两个操作的顺序,有可能JVM给新对象分配了空间,直接赋值给instance变量,然后才去做初始化实例操作。比如下面这种情况

1,A,B两个线程都进入第一个if条件

2,A线程先抢到锁进入到synchronized代码块,执行了instance = new Singleton()这行代码,然后释放锁,此时有可能JVM只给实例对象分配了空白的内存空间,并没有执行初始化操作

3,B线程抢到锁,进入到synchronized代码块,第二次判断的时候发现instance不是null,直接返回使用却发现得到的对象还没有被初始化,于是出现了问题。

再次改进:使用volatile关键字修饰声明的成员变量instance

/**
 * @author: xuzhilei6656
 * @create: 2021-12-12 12:22
 * @description: 单例模式(懒汉式)
 **/
public class Singleton {
    //声明变量,被volatile修饰
    private volatile static Singleton instance;

    //私有构造器
    private Singleton(){
    }

    //获取实例的静态方法
    public static synchronized Singleton getInstance(){
        //第一次检查
        if (instance == null){
            //获取锁
            synchronized (Singleton.class){
                //第二次检查
                if (instance==null){
                    //两次检查都确定没有已存在的实例对象,这才进行对象的创建操作
                    instance = new Singleton();
                }
            }

        }
        return instance;
    }

}

volatile关键字作用:通过volatile修饰的变量,不会被线程本地缓存,所有线程对该对象的读写都会第一时间同步到主内存,从而保证多个线程间该对象的准确性。

这个写法已经比较完美了,既能保证安全的创建出唯一实例,又不会对系统性能有太大影响。

不过,还有更优的写法:静态内部类实现

​
/**
 * @author: xuzhilei6656
 * @create: 2021-12-12 15:17
 * @description: 单例模式
 **/
public class Singleton {

    //私有构造器
    private Singleton() {
    }

    //静态内部类声明实例
    private static class SingletonFactory{
        private static Singleton instance = new Singleton();
    }

    //获取实例的静态方法
    public static Singleton getInstance(){
        return SingletonFactory.instance;
    }

}

​

使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance()方法的时候,JVM能够保证创建出唯一的实例对象,并且这个实例对象是已经被初始化完成的,就解决了上面的线程安全问题

最后一种实现单例的写法也很完美,代码最简洁

通过枚举

/**
 * @author: xuzhilei6656
 * @create: 2021-12-12 15:33
 * @description: 单例模式
 **/
public enum Singleton {

    //代表一个Singleton实例
    INSTANCE;
}

通过枚举来实现单实例代码更加简洁,而且JVM从根本上保证实例对象的唯一性,是更简洁、高效、安全的实现单例的方式 

以上就是浅析Java单例设计模式(自写demo)的详细内容,更多关于Java单例模式的资料请关注我们其它相关文章!

(0)

相关推荐

  • 深入理解Java设计模式之单例模式

    目录 一.什么是单例模式 二.单例模式的应用场景 三.单例模式的优缺点 四.单例模式的实现 1.饿汉式 2.懒汉式 3.双重加锁机制 4.静态初始化 五.总结 一.什么是单例模式 单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在. 许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为.比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息.这种方式简

  • Java中的单例模式详解(完整篇)

    目录 前言 WHAT WHY 饿汉式 实现一:静态实例参数与静态代码块 实现二:静态内部类 懒汉式 错误一:单线程实现 错误二:同步方法 错误三:同步代码块之单次检查 错误四:同步代码块之双重检查 正确:双重检查+阻止重排序 枚举 场景 总结 前言 个人认为单例模式是设计模式中最简单也是最常用的一种,是对有限资源合理利用的一种方式.这个模式看似简单,但是其中蕴含了关于并发.类加载.序列化等一系列深层次的知识,如果理解不够深,就有可能在高并发时遇到难以预期的异常,或者会造成资源浪费. 所以本文会从

  • Java单例模式的6种实现方式详解

    目录 为什么使用单例模式 使用单例模式需要注意的关键点 单例模式的几种写法 1. 饿汉式 2. 懒汉式 3. DCL(Double CheckLock)实现单例 4. 静态内部类 5. 枚举单例 6. 容器实现单例 总结 为什么使用单例模式 需要确保某个类只要一个对象,或创建一个类需要消耗的资源过多,如访问IO和数据库操作等,这时就需要考虑使用单例模式了. 使用单例模式需要注意的关键点 将构造函数访问修饰符设置为private 通过一个静态方法或者枚举返回单例类对象 确保单例类的对象有且只有

  • Java 实例解析单例模式

    目录 单例模式的介绍 优点 缺点 Synchronized Synchronized示例 Synchronized与非Synchronized Singleton 第一个示例 第二个示例 第三个示例 第四个示例 第五个示例 单例模式的介绍 单例对象(Singleton)是一种常用的设计模式.在实际使用中,单例对象能保证在一个JVM中,该对象只存在一个实例存在. 优点 1.减少系统开销,提高系统性能 2.省去了new操作符,降低了系统内存的使用频率,减轻GC压力 3.避免对共享资源的多重占用 缺点

  • Java 单例模式详细解释

    目录 饿汉式 懒汉式 懒汉式(加锁synchronized) 懒汉式(部分加锁synchronized) 懒汉式(DCL) 懒汉式(DCL)最终版 静态内部类 总结 饿汉式 /** * 饿汉式 * 类加载到内存后,就是实例化一个单例,JVM保证线程安全 * 简单使用:推荐使用 * 唯一缺点:不管用与不用,类加载时就会完成实例化 */ public class Demo01 { //开始先新建一个对象 private static final Demo01 INSTANCE = new Demo0

  • Java单例模式分析

    目录 单例模式 为什么要用单例 单例的关键点 几种写法 懒汉式 饿汉式 静态内部类写法 枚举单例 容器实现单例 参考 总结 单例模式 为什么要用单例 确保某个类只有一个对象,常用于访问数据库操作,服务的配置文件等. 单例的关键点 1.默认构造函数为private,复制构造函数和复制赋值函数也要private或=delete禁用.(做到无法被外部其他对象构造) 2.通过一个静态方法或枚举返回单例类对象. 3.确保多线程的环境下,单例类对象只有一个. 几种写法 本文主要介绍C++的懒汉式和饿汉式写法

  • 浅析Java单例设计模式(自写demo)

    目录 单例模式特点 单例模式优点 实现方式 饿汉式(线程安全) 懒汉式 单例模式特点 1.构造器私有 2.在一个Java应用程序中,可保证只有一个实例对象 3.只提供一个供外界调用的getInstance()方法 单例模式优点 1.减少某些对象的频繁创建,降低系统开销和内存占用 2.外部调用不使用new关键字,降低系统内存的使用频率 3.对于特殊的类,在系统中只能存在一个实例,否则系统无法正常运行,比如Controller 实现方式 这里简单介绍两种实现方式 饿汉式(线程安全) /** * @a

  • Java之单例设计模式示例详解

    单例设计模式 保证一个类在内存中只能有一个对象. 思路: 1)如果其他程序能够随意用 new 创建该类对象,那么就无法控制个数.因此,不让其他程序用 new 创建该类的对象. 2)既然不让其他程序 new 该类对象,那么该类在自己内部就要创建一个对象,否则该类就永远无法创建对象了. 3)该类将创建的对象对外(整个系统)提供,让其他程序获取并使用. 饿汉式: 一上来我就把对象给你 new 好了,你来了直接就可以拿去"吃"了 懒汉式 (要是有人问单例的延迟加载方式指的就是这种方式) 一开始

  • java 单例的五种实现方式及其性能分析

    java 单例的五种实现方式及其性能分析 序言 在23种设计模式中,单例是最简单的设计模式,但是也是很常用的设计模式.从单例的五种实现方式中我们可以看到程序员对性能的不懈追求.下面我将分析单例的五种实现方式的优缺点,并对其在多线程环境下的性能进行测试. 实现 单例模式适用于资源占用较多的类,保证一个类只有一个实例即单例.通用的做法就是构造器私有化,提供一个全局的访问点,返回类的实例. uml图: 1.饿汉式 代码实现: package com.zgh.gof23.singleton; /** *

  • 完美解决单例设计模式中懒汉式线程安全的问题

    首先写个单例: public class SingleDemo { private static SingleDemo s = null; private SingleDemo(){} public static SingleDemo getInstance(){ if(s == null){ s = new SingleDemo(); } return s; } } 写个测试类: public class ThreadDemo3 { public static void main(String

  • 浅谈JAVASE单例设计模式

    简单的说设计模式,其实就是对问题行之有效的解决方式.其实它是一种思想. 1.单例设计模式. 解决的问题:就是可以保证一个类在内存中的对象唯一性.(单个实例) 使用单例设计模式需求:必须对于多个程序使用同一个配置信息对象时,就需要保证该对象的唯一性. 如何保证对象唯一性?                                                      解决步骤: 1.不允许其他程序用new创建该对象.                                    

  • Python 单例设计模式用法实例分析

    本文实例讲述了Python 单例设计模式用法.分享给大家供大家参考,具体如下: demo.py(单例): class MusicPlayer(object): # 类属性 记录对象引用 instance = None def __new__(cls, *args, **kwargs): # 1. 判断类属性是否是空对象 if cls.instance is None: # 2. 调用父类的方法,为第一个对象分配空间 cls.instance = super().__new__(cls) # 3.

  • Java多例设计模式实例详解

    本文实例讲述了Java多例设计模式.分享给大家供大家参考,具体如下: 一.多例设计模式定义 多例设计模式就是存在多个对象实例,供外部应用裯用,比喻数据库连接池. 二.多例模式静态类图 三.多例模式代码实现 1. 多例模式核心类 package com.demo.multipleton; import java.util.ArrayList; /** * 多例模式 * * @author * */ public class Multipleton { // 多例数量 private static

  • C++单例设计模式详细讲解

    目录 特殊类设计 只能在堆上创建对象的类 请设计一个类只能在栈上创建对象 请设计一个类不能被拷贝 请设计一个类不能被继承 请设计一个类只能创建一个对象(单例模式) 懒汉模式和饿汉模式的对比 特殊类设计 只能在堆上创建对象的类 请设计一个类,只能在堆上创建对象 实现方式: 将类的构造函数私有,拷贝构造声明成私有.防止别人调用拷贝在栈上生成对象. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建 class test { public: static test* GetObj() { re

  • python单例设计模式实现解析

    这篇文章主要介绍了python单例设计模式实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 所谓单例,就是让类创建对象的时候,在系统中只有唯一的一个实例. (1)定义一个类属性,初始值是None,用于记录单例的引用. (2)重写__new__方法. (3)如果类属性是None,调用父类方法分配空间,并在属性中记录结果. (4)返回属性中记录的对象引用. class MusicPlayer(object): instance = None

  • Unity通用泛型单例设计模式(普通型和继承自MonoBehaviour)

    单例模式是设计模式中最为常见的,不多解释了.但应该尽量避免使用,一般全局管理类才使用单例. 普通泛型单例: public abstract class Singleton<T> where T : class, new() { private static T instance = null; private static readonly object locker = new object(); public static T Instance { get { lock (locker)

随机推荐