我有多个线程在 lucene 索引中执行搜索。在每次搜索之前,都会检查内容是否已被索引,如果没有,则将其添加到索引中。如果同时对未索引的内容进行两次并行搜索,则会出现重复的文档,并且猜测搜索结果会混乱。
我找到了以下方法:IndexWriter.updateDocument
但我认为这并不能解决我面临的多线程问题。
任何如何解决此问题的建议表示赞赏。
我有多个线程在 lucene 索引中执行搜索。在每次搜索之前,都会检查内容是否已被索引,如果没有,则将其添加到索引中。如果同时对未索引的内容进行两次并行搜索,则会出现重复的文档,并且猜测搜索结果会混乱。
我找到了以下方法:IndexWriter.updateDocument
但我认为这并不能解决我面临的多线程问题。
任何如何解决此问题的建议表示赞赏。
首先确保一次只有一个方法(IndexWriter#updateDocument()
)调用调用,你会用属于你的线程的共享对象来实现它,像这样
class Search implements Runnable{
private Object lock=new Object();
private volatile boolean found=false;
public void run(){
//business
if(<<found something!>> && !found){
synchronized(lock){/*call the related-method*/found=true;}
}
//business
}
}
其次,您需要跟踪在搜索过程中找到的每个键以避免重复,可能会检查键或使用简单的布尔检查。
并且请通过向另一个线程发出关于中止搜索过程的信号来提防无用的过程,如果您只需要最初创建的密钥,则它取决于业务。
如果您无法修改更新/添加的来源以更智能地避免重复,那么您将不得不在某处创建一个阻塞点。目标只是尽可能减少争用。
一种方法是使用请求队列、工作队列和 ConcurrentHashMap 进行查找。所有新请求都被添加到由单个“看门人”线程处理的请求队列中。网守可以一次接受一个请求,也可以排空队列并在一个循环中处理所有待处理的请求,以减少该端的争用。
为了处理请求,Gatekeeper 在 ConcurrentHashMap 上执行 putIfAbsent。如果返回值为空,则可以将更新/插入请求添加到实际工作队列中。如果该值已经在地图中,那么....见下面的#2。实际上,由于 putIfAbsent 是原子的,因此您可以使用超过 1 个网守,但这只会增加 HashMap 的争用。网守的实际处理时间是如此之短,以至于您将更多的它们扔到请求队列中并没有真正获得任何收益。
工作队列线程将能够同时处理多个更新/插入,只要它们不修改相同的记录。当工作队列线程完成处理请求时,它们会从 ConcurrentHashMap 中删除该值,以便网守知道再次修改该记录是安全的。
--
需要考虑的一些事情:
1)你想如何定义可以同时做的事情?它可能不应该散列整个请求,因为您不希望两个不同的请求同时修改同一个文档,对吗?
2) 你如何处理由于队列中已经有重复项而当前无法处理的请求(或修改相同文档的请求,如第 1 点)?把它们扔出去?将它们放入定期重试的辅助更新队列中?如果原始请求者的请求处于无限期持有模式,您将如何回应它?
3) 处理请求的顺序是否重要?