繁体   English   中英

抛出异常与同步

[英]Throw exception vs 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;
 }

开始使用method1直到探查器告诉你它是瓶颈。 这是最干净的实现,你知道它会一直正常工作。 如果稍后您看到数据显示您在连续调用时浪费了大量时间,那么您可以考虑尝试其他方法。

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

UPDATE

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

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

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

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

编辑:下面我写了双重检查锁定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() {
    ...
} 

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM