我有一个底层的异步无序查询/响应系统,我想让它同步。可以通过使用唯一ID标记查询来映射查询和响应,而唯一ID又将伴随相应的响应。
我尝试使其同步使用两个ConcurrentHashMap
:一个从ID映射到结果,另一个从相同的ID映射到CountDownLatch
es。执行查询时代码如下:
public Result execute(Query query) throws InterruptedException {
int id = atomicInteger.incrementAndGet();
CountDownLatch latch = new CountDownLatch(1);
latchMap.put(id, latch);
query.executeAsyncWithId(id); // Probably returns before result is ready
try {
latch.await(); // Blocks until result is ready
return resultMap.remove(id);
} catch (InterruptedException e) {
latchMap.remove(id); // We are not waiting anymore
throw e;
}
}
处理传入结果的代码:
public void handleResult(Result result) {
int id = result.getId();
CountDownLatch latch = latchMap.remove(id);
if (latch == null) {
return; // Nobody wants result
}
resultMap.put(id, result);
latch.countDown();
}
从一个线程调用此方法,该线程读取底层系统的所有传入结果(只有一个这样的读取器线程)。
首先,我不确定线程安全性,但似乎没有必要为此使用两个HashMap
(特别是因为ID永远不会被重用)。有任何改进的想法吗?
答案 0 :(得分:4)
受this answer to a similar question启发的新尝试:
public class ResultFuture {
private volatile Result result = null;
private final CountDownLatch latch = new CountDownLatch(1);
public Result get() throws InterruptedException {
latch.await();
return result;
}
public void set(Result result) {
this.result = result;
latch.countDown();
}
}
现在我只需要HashMap
个ResultFuture
中的一个public Result execute(Query query) throws InterruptedException {
int id = atomicInteger.incrementAndGet();
ResultFuture resultFuture = new ResultFuture();
resultFutureMap.put(id, resultFuture);
query.executeAsyncWithId(id); // Probably returns before result is ready
try {
return resultFuture.get(); // Blocks until result is ready
} finally {
resultFutureMap.remove(id);
}
}
public void handleResult(Result result) {
int id = result.getId();
ResultFuture resultFuture = resultFutureMap.get(id);
if (resultFuture == null) {
return; // Nobody wants result
}
resultFuture.set(result);
}
:
{{1}}
答案 1 :(得分:0)
ConcurrentHashMap
的用法意味着您应该只信任在文档中定义为原子的方法,如:
putIfAbsent(K key, V val)
replace(K key, V val)
remove(K key, V val)
因此,如果您计划保留它们,则应更改使用情况,这至少可以保证您的哈希图的线程安全。
除了帽子之外,只需为请求的每个查询创建一个新的Executor
,它返回结果本身,以便线程在继续工作之前等待它完成:这样你就可以完成所有的事情。同步时尚..
答案 2 :(得分:0)
我认为使用CountDownLatch的版本可能是最好的解决方案。 “Java Concurrency in Practice”(Brian Goetz)实际上谈到了这一点(我认为他称之为值锁存器)。它本质上是一个设置值的一次性同步机制。
尽管可以使用wait() - notifyAll()机制实现这样的值锁存器,但使用CountDownLatch可以实现更简单的实现。
CountDownLatch await() - countDown()方法提供了一个合适的先发生关系。