java - 如何使公共(public)静态非同步 getInstance() 方法将私有(private)静态引用变量的多个实例返回给对象?

标签 java multithreading thread-safety synchronized scjp

我遇到的其中一个 SCJP 练习题提供了 SafeDeposit 类中的代码。该问题的答案声称,如果另一个类使用多个线程,则非同步(非线程安全)getInstance() 方法有可能返回 SafeDeposit 的多个实例。我试了又试,但无法获得 toString() 方法来指示创建了不止一个 SafeDeposit 实例。我是否遗漏了什么,或者这只是“可能”发生但真的、真的、真的不太可能发生的事情之一?

class SafeDeposit {
    private static SafeDeposit sd;
    public static SafeDeposit getInstance() {
        if(sd == null) sd = new SafeDeposit();
        return sd;
    }
    private SafeDeposit() { }
}

public class PrivCon {
    public static void main(String[] args) {
        String checker;
        SafeThief wizard = new SafeThief();
        SafeThief wizard2 = new SafeThief();
        for(int i = 0; i < 10; i ++) {
            new Thread(wizard).start();
            new Thread(wizard2).start();
        }
    }
}

class SafeThief implements Runnable {
    public void run() {
        System.out.println(SafeDeposit.getInstance().toString());
    }
}

最佳答案

is this just one of those things that "could" happen but is really, really, really unlikely to happen?

试试这段代码,看看它到底有多不可能:

class SafeDeposit {
  private static SafeDeposit sd;
  public static SafeDeposit getInstance() {
    if(sd == null) sd = new SafeDeposit();
    return sd;
  }
  private SafeDeposit() { }

  static void warmup() {
    for (int i = 0; i < 100_000; i++) getInstance();
    sd = null;
  }
}

public class PrivCon {
  public static void main(String[] args) {
    SafeDeposit.warmup();
    SafeThief wizard = new SafeThief();
    for(int i = 0; i < 10; i ++) new Thread(wizard).start();
  }
}

class SafeThief implements Runnable {
  public void run() {
    try { Thread.sleep(100); } catch (InterruptedException e) {  }
    System.out.println(SafeDeposit.getInstance().toString());
  }
}

这是我的典型输出:

test.SafeDeposit@52e5376a
test.SafeDeposit@34780af5
test.SafeDeposit@351775bc
test.SafeDeposit@2b1be57f
test.SafeDeposit@6ae6235d
test.SafeDeposit@6276e1db
test.SafeDeposit@52e5376a
test.SafeDeposit@302b2c81
test.SafeDeposit@60f00e0f
test.SafeDeposit@1732a4df

几乎没有任何重复。

如果您想知道原因,那是因为我添加了预热代码,这导致 getInstance() 方法被 JIT 编译成一段积极优化的代码它利用了 Java 内存模型提供的自由。

我还在 Runnable 的开头添加了一些 sleep 时间,因为一旦一个线程写入值,那些在该点之后启动的线程将可靠地观察到写。所以最好先让所有线程启动,然后让它们调用getInstance

关于java - 如何使公共(public)静态非同步 getInstance() 方法将私有(private)静态引用变量的多个实例返回给对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20644203/

相关文章:

c# - 开始,停止,暂停和继续运行很长时间的方法

wpf - 立即更新绑定(bind)的 WPF 属性

java - 重新部署应用程序后杀死 Quartz 的线程

java - Android 主线程 Looper 关注点

java - 如何通过以编程方式提供 SELECTALL CHECKBOX 来检查多选警报框中的所有项目

java - 如何从 Java 中的 'double' 类型的值中删除十进制值

java - 如何读取ArrayList中的输入?

java - 分割Java字符串

multithreading - 在实时应用程序中检测死锁的方法

android - 单声道安卓 : terminate thread in onPause() state