是必要的同步线程安全单例的方法

时间:2015-03-08 16:28:40

标签: java multithreading

假设我有一个单身人士:

public class MySinglton {

    private static volatile MySinglton s;
    private int x; 

    public static MySinglton getInstance() {
        if (s != null) return s;
        synchronized (MySinglton.class) {
            if (s == null) {
                s = new MySinglton();
            }
        }
        return s;
    }

    public void setX(int x){
        this.x = x;
    }

} 

好的,方法getInstance是线程安全的。我的问题是。是否有必要修改方法setX,或者它是线程安全的,因为getInsatnce方法是线程安全的。如果不是更好的话。

    synchronized public void setX(int x){
        this.x = x;
    }

    public void setX(int x){
        synchronized (this) {
            this.x = x;
        }
    }

或最后

    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void setX(int x){
        readWriteLock.readLock().lock();
        this.x = x;
        readWriteLock.readLock().unlock();
    }

6 个答案:

答案 0 :(得分:2)

仅仅因为getInstance()是线程安全的,这并不意味着该类的任何其他方法都是线程安全的。多个线程仍然可以同时对单例对象执行操作。

您应该在setX函数内同步私有的最终类成员对象。不同步this并且不同步整个方法。同步this同步对象,如果其他代码也同步getInstance()返回的对象,您可能会陷入僵局。在方法之前放置synchronized关键字意味着在任何给定时间,实例上的线程只能执行类中同步方法的一个同步方法。它还可以给类的客户端/消费者留下印象,即该类是线程安全的,即使它可能不是。

编写单例的一个好方法是使用枚举,因为它保证只有一个实例。但是,如果您希望枚举的成员函数是线程安全的,那么它仍然需要同步。有关此示例,请参阅Effective Java的第311页:http://uet.vnu.edu.vn/~chauttm/e-books/java/Effective.Java.2nd.Edition.May.2008.3000th.Release.pdf

答案 1 :(得分:2)

没有setX不是线程安全的,因为可能有多个线程引用了singleton的实例,并且他们可能会尝试在同一个实例上执行setX api。

您需要制作setX threadsafe。使用synchronized关键字的方法实现都可以。

我不会使用readWriteLock的read lock作为写入值。如果在你获得锁定和解锁之间发生了什么事情呢?你会解锁,从而导致死锁。所以总是在try / catch / finally块中使用锁定和解锁,在finally块中解锁。

答案 2 :(得分:1)

拥有单例并不能防止多个线程同时调用setX()。所以你肯定需要在这里同步。

在写入之前获取READ锁似乎很尴尬。要点是:读者(调用缺失的方法" getX()")需要一个readlock;在写作时,你想要一个WRITE锁!

准确地说:如果更新的属性不是"原子"那么你需要一个ReadWrite锁。和你的节目"知道"不同的读者"和#34;作家"作用。

如果只有" setX()"方法(没有读者,然后"同步"应该这样做;在这种情况下你不需要RW锁定。)

答案 3 :(得分:0)

在s的实例上调用setX。因此在JVM中任何给定时间只有一个s实例(bcos是单例)。如果两个线程同时调用setX,它们可以同时写入相同的x或相互踩踏。

  • 例如。 没有同步,如果一个线程A&线程B在完全相同的时间点更新x,访问这些值的其他线程可能会看到x的不同值。

setX不是隐式线程安全的。

这是一件好事

public void setX(int x){
    synchronized (this) {
        this.x = x;
    }
}

以下是类似的帖子Java fine-grained synchronization in getter/setter methods and singleton pattern of a class

答案 4 :(得分:0)

public final class MySinglton 
{ 
   private final static MySinglton s; 
   private volatile int x; 

   static {
       s = new MySinglton();
   }

   private MySinglton() {}

   public static MySinglton getInstance() 
   {
       return s;
   }

   public void setX(int x)
   {
      this.x = x; 
   }

}

如果您对Singleton没有任何其他要求,您可以使用它。提供一个默认构造函数以避免其他实例并将该类标记为final,因此没有人可以扩展它。 将初始化放在静态块中对大多数程序都可以正常工作。如果遇到问题,只需将其包装在静态内部类中。

答案 5 :(得分:0)

你向我们展示的只是一个带有方法setX(int x)的单例对象。但是,除了设置其他类无法看到的私有变量之外,setX(x)方法不会执行任何操作。

您尚未向我们展示使用 x的任何代码。在你向我们展示如何使用x之前,“线程安全”意味着什么。

“线程安全”就是确保您的程序能够执行您希望它执行的操作,无论其线程的执行如何交错。你希望你的程序做什么?

有时我们会讨论“线程安全”类。例如,人们说java.util.concurrent.ArrayBlockingQueue是“线程安全的”。但这只意味着程序中的线程无法将ArrayBlockingQueue置于错误的内部状态。它 not 意味着队列将始终包含程序希望它包含的对象,或者按程序所需的顺序。使用“线程安全”对象构建程序不会使程序线程安全。

MySingleton课程中还有哪些其他方法?它应该如何使用?