Java 生产者-消费者线程间通信
线程间通信有问题,并通过到处使用“虚拟消息”来“解决”。这是个坏主意吗?可能的解决办法是什么 我有一个例子问题 主线程启动一个线程,用于处理记录并将其插入数据库。 主线程读取一个可能很大的文件,并将一个记录(对象)一个接一个地放入blockingqueue。处理线程从队列中读取数据并执行操作 如何让“处理线程”停止? 队列可以为空,但工作未完成,并且当处理线程完成工作并且不能中断它时,主线程现在也不会 处理线程就是这样Java 生产者-消费者线程间通信,java,concurrency,producer-consumer,Java,Concurrency,Producer Consumer,线程间通信有问题,并通过到处使用“虚拟消息”来“解决”。这是个坏主意吗?可能的解决办法是什么 我有一个例子问题 主线程启动一个线程,用于处理记录并将其插入数据库。 主线程读取一个可能很大的文件,并将一个记录(对象)一个接一个地放入blockingqueue。处理线程从队列中读取数据并执行操作 如何让“处理线程”停止? 队列可以为空,但工作未完成,并且当处理线程完成工作并且不能中断它时,主线程现在也不会 处理线程就是这样 while (queue.size() > 0 || !Thread.
while (queue.size() > 0 || !Thread.currentThread().isInterrupted()) {
MyObject object= queue.poll(100, TimeUnit.MILLISECONDS);
if (object != null) {
String data = object.getData();
if (data.equals("END")) {
break;
}
// do work
}
}
// clean-up
synchronized queue) {
queue.notifyAll();
}
return;
和主线
// ...start processing thread...
while(reader.hasNext(){
// ...read whole file and put data in queue...
}
MyObject dummy = new MyObject();
dummy.setData("END");
queue.put(dummy);
//Note: empty queue here means work is done
while (queue.size() > 0) {
synchronized (queue) {
queue.wait(500); // over-cautios locking prevention i guess
}
}
请注意,插入必须在同一事务中,并且不能处理该事务
通过主线程
有什么更好的方法可以做到这一点?
(我正在学习,不想开始“以错误的方式做”)这些伪消息是有效的。它被称为“毒药”。生产者发送给消费者使其停止的东西 另一种可能是在主线程的某个位置调用Thread.interrupt(),并在工作线程中相应地捕获和处理InterruptedException 通过在所有地方使用“虚拟消息”来“解决”它。这是一辆汽车吗 坏主意?可能的解决办法是什么 这不是一个坏主意,它被称为“毒丸”,是阻止基于线程的服务的合理方式 但它只有在知道生产者和消费者的数量时才起作用 在您发布的代码中,有两个线程,一个是“主线程”,它生成数据,另一个是“处理线程”,它消耗数据,“毒丸”在这种情况下运行良好 但想象一下,如果你还有其他生产商,消费者如何知道何时停止(只有在所有生产商都发送“毒丸”时),你需要知道所有生产商的确切数量,并检查消费者中的“毒丸”数量,如果它等于生产商的数量,这意味着所有生产商都停止工作,然后消费者停止 在“主线程”中,您需要捕获
中断异常
,否则,“主线程”可能无法设置“毒丸”。你可以像下面这样做
...
try {
// do normal processing
} catch (InterruptedException e) { /* fall through */ }
finally {
MyObject dummy = new MyObject();
dummy.setData("END");
...
}
...
此外,您还可以尝试使用执行器服务解决所有问题
(当您只需要做一些工作,然后在完成所有工作后停止时,它就可以工作)
void doWorks(设置工作、长超时、时间单位)
抛出中断异常{
ExecutorService exec=Executors.newCachedThreadPool();
试一试{
用于(最终管柱工程:工程)
exec.execute(新的Runnable(){
公开募捐{
...
}
});
}最后{
exec.shutdown();
执行等待终止(超时,单位);
}
}
我正在学习,不想开始“做错事”
您可能需要阅读这本书:实践中的Java并发。相信我,这是最好的。您可以做的(我在最近的一个项目中做过)是包装队列,然后添加一个'isOpen()'
方法
class ClosableQ<T> {
boolean isOpen = true;
private LinkedBlockingQueue<T> lbq = new LinkedBlockingQueue<T>();
public void put(T someObject) {
if (isOpen) {
lbq.put(someObject);
}
}
public T get() {
if (isOpen) {
return lbq.get(0);
}
}
public boolean isOpen() {
return isOpen;
}
public void open() {
isOpen = true;
}
public void close() {
isOpen = false;
}
}
读者线程:
while (dataQ.isOpen) {
someObject = dataQ.get();
}
当然,您可以扩展列表,但这会为用户提供您可能不想要的访问级别。您需要在代码中添加一些并发性内容,比如AtomicBoolean。Ok。如果传递的对象类型没有一个字符串字段可以轻松用于此操作,我将如何使用此字段?如果任何文本都可以是有效数据,因此可以等同于毒药呢?使用GUID?理想情况下,您应该更改MyObject类并在其中添加方法isPoison()。此方法如何确定对象是否为毒药取决于您,可能是比较消息是否为null、空或消息是否显式创建为毒药之类的简单事情。一个想法是将MyObject重构为一个具有两个实现的接口,一个MessageObject在isPoison()方法中总是返回false,另一个ToxinMessage总是返回true。但这在很大程度上取决于您具体在做什么,考虑到您的实际情况,可能存在更好的解决方案。我知道executor服务,但数据读取、处理(验证)和插入不能在同一个任务或方法中,因为它们都应在同一个数据库事务中运行。如果你明白我的意思。好吧,我明白,
ExecutorService
只是我的一般建议,根据你的要求使用或不使用它。但是要小心捕获所有异常,如果某些异常未捕获,您可能会失去设置“毒丸”的机会。我正在将队列传递给一个方法。读了你的想法,我有以下几点。该方法还可以接受原子布尔keepProcessing。然后只要queue.size>0 | | keepProcessing.get()它就可以运行
while (reader.hasNext() ) {
// read the file and put it into the queue
dataQ.put(someObject);
}
// now we're done
dataQ.close();
while (dataQ.isOpen) {
someObject = dataQ.get();
}