Java 中单例模式的常见实现方式

发布时间:2023年12月20日

目录

一、什么是单例模式?

二、单例模式有什么作用?

三、常见的创建单例模式的方式

?1、饿汉式创建

2、懒汉式创建

3、DCL(Double Checked Lock)双检锁方式创建

3.1、synchronized 同步锁的基本使用

3.2、使用 DCL 中存在的疑问

??????? 3.2.1、为什么不直接在静态方法上加 synchronized 关键字,直接上锁?

??????? 3.2.2、为什么 synchronized 代码块中锁的是当前类对象,为什么不是 this ?


一、什么是单例模式?

????????单例模式是一种创建型设计模式,它确保类只有一个实例,并提供全局访问点让外部代码可以访问该实例。

????????在 Java 中,可以使用单例模式来实现一些全局性的操作,例如配置文件管理线程池管理数据库连接池管理等等。这些操作只需要在程序运行的时候创建一次实例,在整个程序生命周期内都可以通过该实例来访问这些全局资源。

二、单例模式有什么作用?

  1. 避免对不需要的对象进行重复的创建,从而节省系统资源。
  2. 提供一个全局可访问的唯一实例,方便对该实例进行管理和操作。
  3. 确保类只有一个实例存在,避免出现因为实例化多个对象而产生的各种问题,例如状态不一致、资源争抢等问题。
  4. 提供一种常用的解决方案,能够帮助程序员更好地组织和管理代码。

三、常见的创建单例模式的方式

?1、饿汉式创建

//线程安全
class HungrySingleton {
    //在一开始就创建完成对象
    private static HungrySingleton hungrySingleton = new HungrySingleton();

    //私有的构造方法,别的类中无法对该类进行创建
    private HungrySingleton() {
    }

    //使用静态方法,直接使用 类名. 的形式就可以调用该方法
    public static HungrySingleton getInstance() {
        return HungrySingleton.hungrySingleton;
    }
}

2、懒汉式创建

//线程不安全
class LazySingleton {
    //一开始命名了对象,但是并没有创建
    private static LazySingleton lazySingleton;

    private LazySingleton() {
    }

    //当存在多个线程调用该方法,就会导致创建的对象不一致。
    public static LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

3、DCL(Double Checked Lock)双检锁方式创建

//基于懒汉式进行双检锁,线程安全
class DCLSingleton {
    //需要使用 volatile 关键字防止指令重排,因为对象的创建过程中存在着半初始化过程
    private static volatile DCLSingleton dclSingleton;

    private DCLSingleton() {
    }

    public static DCLSingleton getInstance() {
        //第一步:先进行判断对象是否为空,避免了所有线程访问直接就去竞争锁
        if (dclSingleton == null) {
            //第二步:对一个线程加锁,其他线程等待
            synchronized (DCLSingleton.class) {
                /*第三步:会继续判断对象是否创建,
                是为了避免当时有多个线程到达第二步的等待阶段,一旦对象创建完成,锁被释放,
                此时其他的线程就会获取锁,如果不判断就会创建新的对象*/
                if (dclSingleton == null) {
                    dclSingleton = new DCLSingleton();
                }
            }
        }
        return dclSingleton;
    }
}

3.1、synchronized 同步锁的基本使用

/**
 * synchronized的不同使用地点的不同含义。
 * 要保证锁的对象是不会变化的。
 */


public class SynchronizedTest {
    //1.使用在静态方法上,此时锁的对象为当前类对象 => SynchronizedTest.class
    public static synchronized void Test(){

    }

    //2.使用在非静态方法上,此时锁的对象是当前类的对象 => this
    public synchronized void Test1(){
        Object o=new Object();
        //3.synchronized代码块,此时锁的对象是括号中的对象
        synchronized(o){

        }
    }
}

3.2、使用 DCL 中存在的疑问

??????? 3.2.1、为什么不直接在静态方法上加 synchronized 关键字,直接上锁?
/* 在方法上直接使用 synchronized 关键字,是对整个方法都加锁了,
    就算对象已经创建,也会使得每个线程来访问都要进行同步操作,降低效率
    public static synchronized DCLSingleton getInstance(){
        if(dclSingleton==null){
            dclSingleton=new DCLSingleton();
        }
        return dclSingleton;
    }*/
??????? 3.2.2、为什么 synchronized 代码块中锁的是当前类对象,为什么不是 this ?

????????在Java中,synchronized关键字可以用于不同的锁定对象。如果我们在DCL中使用当前类的对象 this 作为锁定对象,那么每个线程都会尝试获取该锁,这样就无法实现同步。因为每个线程都会创建自己的对象实例,而不是共享同一个实例。

????????通过在synchronized代码块中使用当前类对象作为锁定对象,可以保证在多线程环境下只有一个线程能够进入该代码块,从而实现对象的单例模式。这是因为类对象是唯一的,所有线程都可以通过该对象来同步访问代码块。

文章来源:https://blog.csdn.net/qq_58417838/article/details/135078807
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。