【问题标题】:Thread-safe in Singleton pattern which holder members持有者成员的单例模式中的线程安全
【发布时间】:2020-07-17 05:49:00
【问题描述】:

我有这个带有单例模式的示例代码:

class Singleton{
    private static Singleton instance;
  private int count;
  private Singleton(){}
  public static synchronized Singleton getInstance(){
    if(instance==null){
        instance=new Singleton();
    }
    return instance;
  }
  public int getCount(){return count;}
  public void setCount(int count){this.count=count;}
  public static void main(String[] args) throws InterruptedException{
    Thread t1=new Thread(()->{
        while(Singleton.getInstance().getCount()==0){
        //loop
      }
      System.out.println("exist t1 with count="+Singleton.getInstance().getCount());
    });
    t1.start();
    Thread.sleep(1000); //time out to force t1 start before t2
    Thread t2=new Thread(()->{
        Singleton.getInstance().setCount(10000);
    });
    t2.start();
    t1.join();
    t2.join();
  }
}

我有一个问题:方法getCountsetCount 在两个线程t1 中调用,t2 是线程安全的,不是吗?

【问题讨论】:

  • "方法 getCountsetCount 在两个线程 t1 中调用,t2 是线程安全的,不是吗?" - 不,它们不是线程安全的,并且 singleton 在这方面是完全不相关的。假设两个线程都完成了对返回相同实例的Singleton.getInstance() 的调用,并且只有在这之后两个线程才同时调用getCountsetCount。这些方法访问相同的 non-volatile 变量 <instance>.count 而不进行同步,其中一种访问 - 来自 setCount - 是 write 访问。根据定义,这是一场数据竞赛
  • 我认为这是一个正确的答案。如果对对象实例的引用对其他线程可见(例如使用 volatile),则该对象的成员不能是线程安全的。

标签: java multithreading synchronized happens-before


【解决方案1】:

在两个线程t1、t2中调用的getCount/setCount方法是线程安全的,不是吗?

如果您打算将 t2 所做的更改 可见 到 t1 - 是的,线程 2 设置的计数将对线程 t1 可见。

这是因为线程 1 每次都通过调用 getInstance 这是一个 synchronized 方法来获取单例实例。这建立了 happens-before 关系,线程 2 所做的更改将对线程 1 可见。


但是,如果您将代码更改为仅调用一次 getInstance 并使用该引用调用 getCount,则另一个线程 (t2) 所做的更改可能对线程 t1 不可见,并且它可以继续循环.

 Singleton s = Singleton.getInstance();
    while(s.getCount()==0){
        //loop
    }

要使更改得到反映,您必须将 count 设为 volatile

private volatile int count;

【讨论】:

  • 在happens-before关系中,线程t2可以看到同步解锁后线程t1可以看到所有更改。但是,当线程t2调用Main.getInstance()时,该方法调用之前的所有更改对线程t1都是可见的,但是setCount是在同步解锁之后调用的,所以count对线程t1是不可见的.
  • t1 每次调用getInstance 时都会进入一个synchronized 块。
  • 但是t1 只能看到t2t2 解锁之前所做的更改,并按照Java 内存模型规则进行同步。
  • 没有什么能比得上同步解锁了。无法理解您的观点
猜你喜欢
  • 2010-09-07
  • 2014-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-27
  • 2015-08-26
  • 2011-02-24
相关资源
最近更新 更多