抛出异常与同步

时间:2011-01-07 16:14:22

标签: java exception synchronized

我有一个方法,许多线程并行访问,该方法使用一个具有两个我无法控制的同步方法的类。 getObject和createNewObject。我想确保我不创建多个对象(MyObject)。

MyObject obj;
public void method1() {
   obj = getObject("key");
   if (obj == null)
      obj = createNewObject("key");
  }
我认为,这不会起作用,因为线程可以在获取和创建方法之间暂停,因此另一个线程也可以进入并创建一个对象。同步的createNewObject方法通过检查一个名为“key”的对象是否已存在并在这种情况下抛出异常来解决此问题。

以下哪种方法会受到优先考虑?性能,安全性和设计明智。我听说双锁型(方法3)不起作用?也许我应该使用method1?

大多数时候,会找到该对象,所以没有问题。在极少数情况下跳过同步并处理异常可能会有更好的性能吗?

MyObject obj;
public synchronized void method1() {
   obj = getObject("key");
   if (obj == null)
      obj = createNewObject("key");
  }

public void method2() {
   obj = getObject("key");
   if (obj == null)
       try {
          obj = createNewObject("key");
       } catch (Exception e) { // ops, someone already created object "key"
            obj = getObject();
       }
  }

public void method3() {
   obj = getObject("key");
   if (obj == null)
       obj = getObj("key");
}
public synchronized MyObject getObj(String key) {
    MyObject obj = getObject(key);
    if (obj == null)
        obj = createNewObject(key);
    return obj;
 }

6 个答案:

答案 0 :(得分:8)

开始使用method1,直到探查器告诉您它是瓶颈。这是最干净的实现,你知道它会一直正常工作。如果以后你看到的数据表明你在连续的电话中浪费了很多时间,那么你可以考虑尝试别的东西。

答案 1 :(得分:4)

这需要一些测试和分析,但我很确定你不会通过使用任何技巧获得任何重要的性能,因为在任何情况下都会执行同步,因为你每次调用getObject()方法,这是同步。所以这不是“同步/不同步”的那种差异,而是“同步/双同步”,这应该不是那么多。如果你正在进行同步,那么最好完全做到这一点。这意味着你的案例中的method1()。

<强>更新

虽然method2()看起来也很有希望,但我刚刚意识到它有问题:因为它没有将写入同步到obj字段,所以其他线程可能看不到它的更新值。因此,如果除了调用method2()的线程之外的其他线程访问obj字段,则method2()不正确。

如果你将obj字段设置为volatile,我相信它可能会起作用(尽管不是100%肯定),因为getObject()是同步的,因此不存在“对非易失性对象的易失性引用”问题。在getObject()返回后,它会执行写入屏障,因此可以保证在主内存中存在完全初始化的对象。并且由于没有线程具有该对象的本地缓存副本,因此任何线程都可以访问obj字段。除非obj字段引用的对象是可变的,否则无论如何都应该同步对它的所有访问。

但是,这仍然没有多大意义。完全不同步的读取访问仍然是不可能的,因此干净的实现仍然比“智能”实现更好。

答案 2 :(得分:3)

现代虚拟机中的同步消耗的资源/执行时间非常少。我只是简单地同步检查/创建方法。过早优化会花费你很多时间/心痛,如果成为问题,你最好不要担心这类事情。

答案 3 :(得分:0)

编辑:下面我写了双重检查锁定ideom,但它有一些重大缺陷,在

中有描述

----原帖如下----

最好的解决方案是:

Object obj;

public Object getObject() {
    if( obj == null ) {
        synchronized(this) { // or on something else
            if( obj == null ) {
                obj = createObject();
            }
        }
    }
    return obj;  
}   

private Object createObject() {
    ...
} 

它的优点是同步仅在对象的关键创建阶段发生,但仍然有效。

答案 4 :(得分:0)

你说你无法控制createNewObject,所以在这种情况下,method1是正确的答案,我只是赞成了这样说的人。但听起来像createNewObject的设计很糟糕。如果它要检查对象是否已经存在,那么在这种情况下它应该返回该对象而不是抛出异常。要求调用者检查对象是否存在,以及是否调用函数然后重复检查对象是否存在,这是愚蠢的。

答案 5 :(得分:0)

在阅读了一下之后,我相信一个真正最好的答案是使用Initialize-On-Demand Holder Class习语:

private static class LazySomethingHolder {
    public static Something something = new Something();
}

public static Something getInstance() {
    return LazySomethingHolder.something;
}

它没有并发问题,即使是公共路径上的volatile变量也没有锁定。