[英]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.