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