[英]Threadsafe bidirectional association in Java
实现线程安全的双向关联有什么好方法? 是否有一个好的库或代码生成器?
这是一个非线程安全的示例:
class Foo {
private Foo other;
public Foo getOther() {
return other;
}
public void setOther(Foo other) {
this.setOtherSecretly(other);
other.setotherSecretly(this);
}
void setOtherSecretly(Foo other) {
if (this.other != null) this.other.other = null;
this.other = other;
}
}
我对线程安全的要求是:
setOther
时, assert foo.getOther().getOther() == foo
是可以接受的。 setOther
而没有其他其他线程覆盖该值,则getOther
立即返回该线程的新值。 getOther
观察到新值,它将永远不会再次接收旧值(除非再次设置)。 也很高兴有:
我的应用程序将有16个线程处理大约5.000个几个类的对象。
我还没有想出一个解决方案(不,这不是功课),所以任何输入(想法,文章,代码)都是受欢迎的。
Google Guava为您做到这一点: BiMap 。
例如:
BiMap<Integer, String> bimap = Synchronized.biMap(HashBiMap.create(), someMutexObject);
bimap.put(1, "one");
bimap.put(2, "two");
bimap.get(1); // returns "one"
bimap.inverse().get("one") // returns 1
someMutexObject
可以是您想要synchronize
任何对象。
您可以将每个对象关联到自己的锁定,然后在获取两个锁定时设置另一个对象。 例如。 为避免死锁,您可以使用锁定顺序
class Foo extends ReentrantLock {
private static final AtomicInteger order = new AtomicInteger(0);
final int id = order.incrementAndGet();
private Foo other;
public Foo getOther() {
return other;
}
public void setOther(Foo other) {
if (id > other.id) {
other.lock();
try {
this.lock();
try {
// assign here
} finally {
this.unlock();
}
} finally {
other.unlock();
}
} else if (id < other.id) {
this.lock();
try {
other.lock();
try {
// assign here
} finally {
other.unlock();
}
} finally {
this.unlock();
}
}
}
}
试试这个,在没有写完的情况下允许阅读。
另一种选择是简单地使other
参考不稳定。 这将满足您的要求和您的好处。
我可以想到一个静态成员作为一个监视器。 但也许这就是你认为'全球'的锁定。
class Foo {
private static final Object MONITOR = new Object();
private Foo other;
public Foo getOther() {
synchronized(MONITOR){
return other;
}
}
public void setOther(Foo other) {
synchronized(MONITOR){
this.setOtherSecretly(other);
other.setotherSecretly(this);
}
}
void setOtherSecretly(Foo other) {
if (this.other != null) this.other.other = null;
this.other = other;
}
}
事实证明这是一个非常难的问题! (很好!)使用全局锁定太简单了,而且可能太慢了。 我想我有一个无锁版本 - 我将在下面介绍 - 但我不会过分相信它是完美的。 所有可能的交错都很难推理。
事实证明,这是事务性内存的完美用例! 只需将整个块标记为原子并修改您想要的任何内容! 你可能会看看Deuce STM ,虽然我不知道它有多快。 如果只有最好的系统不需要定制硬件......
无论如何,在思考了这个问题一段时间之后,我想我想出了一个使用Java的AtomicReference来绕过锁的版本。 一,代码:
class Foo {
private AtomicReference<Foo> oRef = new AtomicReference<Foo>;
private static final AtomicInteger order = new AtomicInteger(0);
private final int id = order.incrementAndGet();
private static bool break(Foo x, Foo y) {
if (x.id > y.id)
return break(y, x);
return x.oRef.compareAndSet(y, null) &&
y.oRef.compareAndSet(x, null);
}
public void setOther(Foo f) {
if (f != null && f.id > id) {
f.setOther(this);
return;
}
do {
Foo other = oRef.get();
if (other == f)
break;
if (other != null && !break(this, other))
continue;
if (f == null)
break;
Foo fother = f.oRef.get();
if (fother != null && !break(f, fother))
continue;
if (!f.oRef.compareAndSet(null, this))
continue;
if (!oRef.compareAndSet(null, f)) {
f.oRef.set(null);
continue;
}
} while (false);
}
}
关键点:
x.oRef.compareAndSet(y, null)
。 f.oRef.compareAndSet(null, f)
成功,则没有其他线程能够破坏break()
的半成熟关系。 然后,如果oRef.compareAndSet(null, f)
成功,则操作完成。 如果失败,可以重置f.oRef
并重试每个人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.