我有一个生成器应用程序,它生成一个索引(将其存储在一些内存中的树数据结构中)。消费者应用程序将使用索引来搜索部分匹配。
我不希望消费者UI在生产者索引数据时必须阻止(例如通过某些进度条)。基本上,如果用户希望使用部分索引,它就会这样做。在这种情况下,生产者可能必须停止索引一段时间,直到用户离开另一个屏幕。
粗略地说,我知道我需要等待/通知协议来实现这一目标。我的问题:是否有可能在执行业务时使用wait / notify中断生产者线程?我需要什么java.util.concurrent原语来实现这个目标?
答案 0 :(得分:2)
您描述的方式,没有理由需要等待/通知。只需同步访问您的数据结构,以确保它在访问时处于一致状态。
编辑:通过“同步访问”,我并不是指同步整个数据结构(最终会阻塞生产者或消费者)。而是仅同步正在更新的那些位,并且仅在更新它们时同步。您会发现生产者的大多数工作都可以以不同步的方式进行:例如,如果您正在构建树,您可以识别插入需要发生的节点,在该节点上同步,执行插入,然后继续。
答案 1 :(得分:0)
生产者应用程序可以有两个索引:已发布和正在工作。生产者只能在工作中工作,消费者只能在已发布的情况下工作。一旦生产者完成索引,它就可以用已发布的(通常交换一个指针)替换工作中的一个。如果产生价值,生产者也可以发布部分索引的副本。通过这种方式,您将避免长期锁定 - 当消费者丢失索引时,它将非常有用。
答案 2 :(得分:0)
不,那是不可能的。
在线程本身没有任何显式代码的情况下通知线程的唯一方法是使用Thread.interrupt(),这将导致线程中的异常。 interrrupt()通常不是很可靠,因为在代码中的某个随机点抛出异常是在所有代码路径中正确的噩梦。除此之外,线程中的某个try {} catch(Throwable){}(包括您使用的任何库)都足以吞下信号。
在大多数情况下,唯一正确的解决方案是使用共享标志或消费者可用于将消息传递给生产者的队列。如果您担心生产者没有响应或冻结,请在单独的线程中运行它并要求它每隔n秒发送一次心跳消息。如果它没有发送心跳,请将其删除。 (请注意,确定制作人是否实际上正在冻结,而不仅仅是等待外部事件,通常很难做到正确)。
答案 3 :(得分:0)
在生产者线程中,您可能会遇到某种主循环。这可能是打断你的制片人的最佳场所。我建议您使用java 5中引入的java同步对象,而不是使用wait()和notify()。
你可能会做那样的事情
class Indexer {
Lock lock = new ReentrantLock();
public void index(){
while(somecondition){
this.lock.lock();
try{
// perform one indexing step
}finally{
lock.unlock();
}
}
}
public Item lookup(){
this.lock.lock();
try{
// perform your lookup
}finally{
lock.unlock();
}
}
}
您需要确保每次索引器释放锁定时,您的索引都处于一致的合法状态。在这种情况下,当索引器释放锁时,它有机会进行新的或等待的lookup()操作来获取锁,完成并释放锁,此时您的索引器可以继续执行下一步。如果当前没有查找()正在等待,那么您的索引器只需重新获取锁本身并继续其下一步操作。
如果您认为可能有更多的线程同时尝试进行查找,您可能需要查看ReadWriteLock接口和ReentrantReadWriteLock实现。
当然,这种解决方案是一种简单的方法。它将阻止其中一个没有锁的线程。您可能想要检查是否可以直接同步数据结构,但这可能会很棘手,因为构建索引往往会使用某种平衡树或B-Tree或其他节点插入远不是微不足道的东西。
我建议你首先尝试这种简单的方法,然后看看它的行为方式是否适合你。如果没有,您可以尝试将索引步骤分解为更小的步骤,或者尝试仅对数据结构的某些部分进行同步。
不要太担心锁定的性能,在java无竞争锁定中(当只有一个线程试图获取锁定时)很便宜。只要您的大多数锁定都不受限制,锁定性能就无需担心。