【问题标题】:Difference between synchronized(Singleton.class) and synchronized(obj)同步(Singleton.class)和同步(obj)之间的区别
【发布时间】:2014-03-11 18:28:56
【问题描述】:

使用以下两个版本的Singleton Classes有什么区别

首先我使用的是 synchronized(Singleton.class)

第二次我使用 synchronized(Obj) //第一个类型 公共类单例 {

private static Singleton obj = null;

Singleton() {
}

public static Singleton getInstance() {

    synchronized(Singleton.class){
        if (obj == null) {
        obj = new Singleton();
    }
}
    return obj;
}

}

//第二种类型

public class Singleton {

private static Singleton obj = null;

Singleton() {
}

public static Singleton getInstance() {

    synchronized(obj){
        if (obj == null) {
        obj = new Singleton();
    }
}
    return obj;
}
}

【问题讨论】:

  • 在第二个示例中同步 null 没有意义
  • 嗯,您确实在第二个版本中意识到,在第一次通话时您将在null 上同步?为什么不在静态初始化器中初始化你的单例呢?
  • 我是多线程新手,所以不能真正理解 synchronized() 的作用?我的天真理解是当我们使用同步()时,我们应该输入我们想要同步的对象/变量,bt 我在下面的帖子中看到我们想要同步“obj”但是我们将syncRoot作为参数传递或通用对象作为参数。 syncRoot 甚至没有改变状态,也没有在块中使用。或者,即使 Singleton.class 也没有指定要同步的对象或变量,只指定类型,那么我们如何知道要同步的块/变量 r。

标签: java synchronization thread-safety singleton


【解决方案1】:

主要区别在于synchronized(obj) 不起作用:第一次调用它时,objnull,所以你会看到NullPointerException

Demo on ideone.

另一方面,Singleton.class 绝不是null,因此您可以使用它进行同步。但是,恶意代码可以执行一种攻击,使您的getInstance 方法永远等待:他们所需要的只是锁定您的Singleton.class,并在那里无限等待。

针对这种攻击的常见防御措施是为您的锁使用单独的私有静态对象,如下所示:

public class Singleton {

    private static Singleton obj = null;
    private static final Object syncRoot = new Object();

    Singleton() {
    }

    public static Singleton getInstance() {
        synchronized(syncRoot){
            if (obj == null) {
                obj = new Singleton();
            }
        }
        return obj;
    }
}

【讨论】:

  • 我是多线程新手,所以不能真正理解 synchronized(syncRoot) 的作用?我幼稚的理解是,当我们使用 synchronized() 时,我们应该输入我们想要同步的对象/变量,但是我看到这里我们想要同步“obj”但是我们将 syncRoot 作为参数传递。 syncRoot 甚至没有改变状态,也没有在块中使用。或者,即使 Singleton.class 也没有指定要同步的对象或变量,只指定类型,那么我们如何知道哪些块/变量是/将要同步的。
  • @Sam synchronized 构造接受一个对象 - 任何对象,并将其用作锁。由您决定要锁定哪些操作。 Java 保证只要对象相同,在该对象的 synchronized 块内最多将执行一个线程。该对象是什么并不重要,只要它不为空即可。我将对象设为私有,以确保其他类无法合法访问它。
  • 我有一个后续问题,习语 Classname.class 返回什么,它是否返回一个对象,我在某处读到它是一个静态编译时调用,但无法理解这是什么意思?在上面提到的案例(Singleton.class)中,因为这是单例,我们如何使用 Singleton.class 创建一个默认对象,如果这就是发生的事情。也非常感谢您解释同步方面。
  • @Sam Classname.class 语法使您可以获取表示类Classname 的对象。 Class 对象的“真正”目的是让您在运行时发现特定类的方法和字段是什么。作为一个习惯用法,Classname.class 有时用于 synchronize 块中以保护对每个类对象的访问,因为 Java 保证每个加载到 JVM 的类只有一个类对象。
  • 你能帮我解决这个问题吗?我吓了一跳,这是一个棘手的问题。 stackoverflow.com/questions/22846484/…
【解决方案2】:

第二个版本尝试在null 上进行同步,但会失败。 synchronized 关键字试图获取obj 引用的Object 上的锁,因为它最初是null,所以你会得到一个NulPointerException

【讨论】:

    【解决方案3】:

    它们实际上是相同的,也是一个糟糕的实现,因为 obj IS NULL (在您的示例中)并且代码单线程每次调用它。它应该使用双重检查锁定...

    第二个应该是:

    public class Singleton {
       private static object syncRoot = new object();
       private static Singleton obj = null
    
       Singleton() {
       }
    
       public static Singleton getInstance() {
    
          if ( obj == null ){
            //ONLY SINGLE THREAD IF obj == null
            synchronized(syncRoot){
              if ( obj == null ){
                 obj = new Singleton();            
              }
            }
          }
          return obj;
       }
    }
    
    【解决方案4】:

    当您使用 synchronized(Singleton.class) 时,同步只会在第一次创建实例之前应用,并且在我们有可能通过我们的代码运行多个线程的情况下使用它。如果已经有一个实例可用,您的 if 循环将负责不创建另一个实例,通过使用这种同步方式,我们减少了很多开销,因为我们知道同步类在时间/等待方面会增加开销。

    【讨论】:

      猜你喜欢
      • 2021-10-16
      • 2014-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-25
      • 2017-08-14
      • 2014-09-30
      • 2014-10-10
      相关资源
      最近更新 更多