当前位置: 首页 > 面试题库 >

单例类方法的并发调用

钱季
2023-03-14
问题内容

我有一个单例课程:

public class Singleton {
    private static Singleton istance = null;

    private Singleton() {}

    public synchronized static Singleton getSingleton() {
        if (istance == null)
            istance = new Singleton();
        return istance;
    }

    public void work(){
            for(int i=0; i<10000; i++){
                Log.d("-----------", ""+i);
            }
    }
}

并且多个线程正在调用work()函数:

public class Main {

public static void main(String[] args) {

    new Thread (new Runnable(){
        public void run(){
            Singleton s = Singleton.getSingleton();
            s.work();}
    }).start();

    System.out.println("main thread");

    new Thread(new Runnable() { 
         public void run() {
             Singleton s = Singleton.getSingleton();
                s.work();
         }
    }).start();
}
}

我注意到两个线程正在同时运行,就好像两个工作函数是同时实例化的一样。

我希望最后一个线程代替上一个线程运行,而不是同时运行。在Java中是否可以使第二个调用覆盖第一个调用的内存空间?


问题答案:

您的getSingleton()方法正在尝试延迟初始化
SINGLETON实例,但存在以下问题:

  • 不能访问变量 synchronized
  • 变量不是 volatile
  • 您没有使用双重检查锁定

因此竞争条件AMY会导致创建两个实例。

最好和最简单的方法是安全地懒惰地初始化一个单例 而不 同步,如下所示:

private static class Holder {
    static Singleton instance = new Singleton();
}

public static Singleton getSingleton() { // Note: "synchronized" not needed
    return Holder.instance;
}

这是线程安全的,因为java类加载器的约定是所有类在使用之前都必须完成其静态初始化。同样,类加载器在被引用之前不会加载类。如果getSingleton()同时调用两个线程,则Holder该类仍将仅加载一次,因此new Singleton()将仅执行一次。

这仍然是懒惰的,因为Holder类是 从引用getSingleton()的方法,所以Holder在第一次呼叫时类只会被加载
getSingleton()而成。

不需要同步,因为此代码依赖于类加载器的内部同步,这是防弹的。

此代码模式是单例飞行的唯一方法。它是:

  • 最快(无同步)
  • 最安全(取决于工业强度等级的装载机安全性)
  • 最干净的(最少的代码-双重检查的锁定很丑陋,并且它的功能很多行)

另一种类似的代码模式(既安全又快速)是在enum单个实例中使用,但是我发现这样做很笨拙,目的并不明确。



 类似资料:
  • 本文向大家介绍php创建类并调用的实例方法,包括了php创建类并调用的实例方法的使用技巧和注意事项,需要的朋友参考一下 PHP类定义 PHP 定义类通常语法格式如下: 解析如下: 类使用 class 关键字后加上类名定义。 类名后的一对大括号({})内可以定义变量和方法。 类的变量使用 var 来声明, 变量也可以初始化值。 函数定义类似 PHP 函数的定义,但函数只能通过该类及其实例化的对象访问

  • 本文向大家介绍Js调用Java方法并互相传参的简单实例,包括了Js调用Java方法并互相传参的简单实例的使用技巧和注意事项,需要的朋友参考一下 Js通过PhoneGap调用Java方法并互相传参的。 一、JAVA代码 写一个类,该类继承自Plugin并重写execute方法。 三、Javascript文件中注册插件 新建一个.js文件,并把该文件和phonegap文件放在同一目录。(新建一个sim

  • 通过前面的学习,类方法大体分为 3 类,分别是类方法、实例方法和静态方法,其中实例方法用的是最多的。我们知道,实例方法的调用方式其实有 2 种,既可以采用类对象调用,也可以直接通过类名调用。 通常情况下,我们习惯使用类对象调用类中的实例方法。但如果想用类调用实例方法,不能像如下这样: 运行上面代码,程序会报出如下错误: Traceback (most recent call last):   Fi

  • 我是JPA和EJB方面的新手。我读过这样一句话: 单例会话bean可以使用容器管理或bean管理的并发。默认值是容器管理的,它对应于所有业务方法上的写锁定。所有业务方法调用都被序列化,以便在任何给定时间只有一个客户端可以访问bean。 有人能简单地解释一下这意味着什么:所有业务方法调用都是序列化的。

  • 假设我有以下代码: 是否有可能在中调用来自的的编译器错误?我知道实际上你会把那个方法放在构造函数中,但这个问题是出于好奇。

  • 本文向大家介绍Go并发调用的超时处理的方法,包括了Go并发调用的超时处理的方法的使用技巧和注意事项,需要的朋友参考一下 之前有聊过 golang 的协程,我发觉似乎还很理论,特别是在并发安全上,所以特结合网上的一些例子,来试验下go routine中 的 channel, select, context 的妙用。 场景-微服务调用 我们用 gin(一个web框架) 作为处理请求的工具,需求是这样的