【发布时间】:2013-10-31 11:20:45
【问题描述】:
我想解析一个文件并将内容传输到数据库中。为了加快一切,文件应该被并行解析。
我有一个主线程,它逐行读取文件并创建 Runnable,将其提供给 ThreadPoolExecutor。每个 Runnable 都有自己的 Session。
每一行都包含一个客户端的唯一标识符,因此可以重复。系统尝试通过标识符在数据库中查找客户端。
如果找不到想要相同客户端的线程之一,则需要创建客户端。我在这里有一个“加入”点,其他线程必须等待允许创建客户端的线程。
c = (Client) s.get("Client", identfier);
if (c == null) {
CountDownLatch lock = isClientResolutionActive(identfier);
if (lock != null) {
lock.await();
LOGGER.info("Lock was released ... " + identfier);
c = (Client) s.get("Client", identfier);
}
}
if (c == null) {
c = createClient(...);
s.save(c);
s.flush();
removeClientResolutionActive(identfier);
}
为了同步它们,我在调用者类中创建了两个方法,一个方法专用于检查是否已经有人在创建客户端并返回共享对象,另一个从列表中删除条目并通知所有等待线程.
我在互联网上搜索了很多,并试图找到我的问题或类似的问题,但没有成功。
此外,我不确定应该使用哪个并发对象。经过研究,我决定使用 CountDownLatch。它用 1 初始化。应该只有一个线程创建它。 (或许除了 CountDownLatch 之外,使用其他东西会更好,但我不知道是什么)
上述方法在地图上包含一个同步块,它保存着客户端的标识符和CountDownLatch的实例。
private CountDownLatch isClientResolutionActive(String identfier) {
synchronized (activeSearches) {
if (activeSearches.containsKey(identfier)) {
// Only create the CountDownLatch if there are multiple threads for
// that identfier
if (activeSearches.get(identfier) == null) {
activeSearches.put(identfier, new CountDownLatch(1));
}
return activeSearches.get(identfier);
} else {
LOGGER.info("Locked " + identfier);
activeSearches.put(identfier, null);
return null;
}
}
}
private void removeClientResolutionActive(String identfier) {
synchronized (activeSearches) {
CountDownLatch cl = activeSearches.get(identfier);
activeSearches.remove(identfier);
if (cl != null) {
LOGGER.info("Unlock " + identfier);
cl.countDown();
}
}
}
通常它工作正常,但有时我会遇到问题,当锁存器被释放(和删除)并且访问同步变量队列包含另一个线程来搜索删除的条目(检查是否有任何线程是已经这样做了),它会尝试再次创建一个新客户端。
18:02:55,611 [pool-1-thread-2] INFO LogImporter Unlock b42fcae346fbb2b1e3c544fb816de2c5
18:02:55,611 [pool-1-thread-3] INFO LogImporter Locked b42fcae346fbb2b1e3c544fb816de2c5
18:02:55,611 [pool-1-thread-4] INFO LogImporter Lock was released ... b42fcae346fbb2b1e3c544fb816de2c5
我认为我必须改进同步,但我不知道如何。
一个想法是将客户端搜索移动到同步块中,或者在再次锁定数据库之前进行检查。
也许创建一个缓存或映射,其中包含数据库中所有已知的客户端。
还是在应用的整个生命周期中只使用一个 Session?
提前感谢任何建议和提示。
【问题讨论】:
-
我不明白为什么在 removeClientResolutionActive 中即使 CountDownLatch 不为零,您也会删除它!?
-
另外,我不明白您为什么不为第一次通话创建 CounDownLatch。就像您尝试将其设置为“null”,就像您想保留一些内存一样。
-
删除后我调用
countdown方法。我有一些线程,但这并不意味着所有线程都需要同一个客户端。如果有第二次尝试接听电话,您就知道有两个线程。 -
但是第二个或第一个(或第三个)线程将删除倒计时。所以没用。下一个线程将找不到它。 (我谈论的是在同一个客户端上工作的线程)。
-
这个想法是只有一个线程可以创建客户端并移除闩锁。为此在
lock.await之后有一个get。
标签: java multithreading hibernate concurrency