【问题标题】:Java synchronize in singleton patternJava以单例模式同步
【发布时间】:2013-04-10 15:51:55
【问题描述】:

像这样实现单例模式的类的每个方法都需要使用 synchronize 关键字吗?

public class Singleton {

  private Singleton(){}

  public synchronized static Singleton getInstance()
    {   
        if(instance == null)
            instance = new Singleton ();

        return instance;
    }

  public void DoA(){
  }
}

由于 Singleton 不公开公共构造函数并且 getInstance() 方法是同步的,因此不需要同步方法 DoA 和 Singleton 类公开的任何其他公共方法。

这个推理正确吗?

【问题讨论】:

  • En 一般同步取决于您是否访问方法中的共享数据。
  • 我不这么认为,如果 getInstance 是同步的。这并不意味着 DoA 是同步的。也是。
  • 如果您在 DoA 方法上改变可共享状态,那么是的,它可能仍需要同步。该方法有什么作用?
  • 这跟单身没关系

标签: java synchronization singleton


【解决方案1】:

它就像任何其他课程一样。它可能需要也可能不需要进一步同步。

考虑以下示例:

public class Singleton {

  private Singleton() {}

  public synchronized static Singleton getInstance() { ... }

  private int counter = 0;

  public void addToCounter(int val) {
    counter += val;
  }
}

如果要从多个线程中使用该类,addToCounter() 有一个竞争条件。解决此问题的一种方法是使 addToCounter() 同步:

  public synchronized void addToCounter(int val) {
    count += val;
  }

还有其他方法可以解决竞争条件,例如使用AtomicInteger

  private final AtomicInteger counter = new AtomicInteger(0);

  public void addToCounter(int val) {
    counter.addAndGet(val);
  }

在这里,我们在不使用 synchronized 的情况下修复了竞态条件。

【讨论】:

  • 如何解决 getInstance 上的 PMD 错误“使用块级而不是方法级同步”?
【解决方案2】:

好吧,Singleton 类的目的是它最多只有一个实例,并且所有线程都可以访问同一个对象。

如果您不同步 getInstance 方法,可能会发生以下情况

线程1进入getInstance()

线程2进入getInstance()

Thread1 将 instance == null 评估为 true

Thread2 将 instance == null 评估为 true

Thread1 分配instance 并返回

Thread2 重新分配instance = new Singleton() 并返回。

现在两个线程都有一个 Singleton 类的不同实例,这应该是这种模式所阻止的。

同步可以防止两个线程同时访问同一个代码块。所以在多线程环境中实例化单例类时需要同步。

现在假设多个线程将尝试同时访问 Singletons 方法,这些方法也可能需要同步。特别是如果他们更改数据而不是只读取数据,这是真的。

【讨论】:

  • 我认为这个问题是指单例实例的公共方法,而不是静态getInstance
  • 是的,我应该更有耐心,在回答之前阅读问题。我会扩展答案...
【解决方案3】:

使用单例的正确(实际上是最好的)方法

private static singleton getInstance() {
    if (minstance == null) {
        synchronized (singleton.class) {
            if (minstance == null) {
                minstance = new singleton();
            }
        }
    }
    return minstance;
}

【讨论】:

  • 如果 minstance 恰好为空,那么将块与该实例同步将导致 NullPointerException
  • 是的。其实我之前的代码写的很匆忙。 ;)
  • 这是一种不好的做法 - 它是双重检查锁定模式 - 毕竟不会起作用 - 在这里解释 cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
  • @PawełWoźniak:放弃你那里有趣的分享。但是对于 java,我认为这是正确的做法!
【解决方案4】:

延迟初始化和线程安全解决方案:

public class Singleton {

    public static class SingletonHolder {
        public static final Singleton HOLDER_INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.HOLDER_INSTANCE;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-25
    • 2015-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多