Java 生产者-消费;消费者如何停止?

Java 生产者-消费;消费者如何停止?,java,producer-consumer,Java,Producer Consumer,所以我模拟了我的生产者消费者问题,我有下面的代码。我的问题是:如果消费者处于常数状态,那么他如何停止(true) 在下面的代码中,我添加了 if (queue.peek()==null) Thread.currentThread().interrupt(); 这在本例中非常有效。但在我的现实设计中,这不起作用(有时生产者“放置”数据需要更长的时间,因此在使用者中引发的异常是不正确的。一般来说,我知道我可以

所以我模拟了我的生产者消费者问题,我有下面的代码。我的问题是:如果消费者处于常数状态,那么他如何停止(true)

在下面的代码中,我添加了

                    if (queue.peek()==null)
                         Thread.currentThread().interrupt();
这在本例中非常有效。但在我的现实设计中,这不起作用(有时生产者“放置”数据需要更长的时间,因此在使用者中引发的异常是不正确的。一般来说,我知道我可以放置“有毒”数据,例如对象是XYZ,我可以在使用者中检查它。但这种有毒会使代码看起来很糟糕。不知道是否有人有不同的方法

public class ConsumerThread implements Runnable
{
 private BlockingQueue<Integer> queue;
 private String name;
 private boolean isFirstTimeConsuming = true;
 public ConsumerThread(String name, BlockingQueue<Integer> queue)
 {
    this.queue=queue;
    this.name=name;
 }

@Override
public void run()
{
    try
    {       
        while (true)
        {   
            if (isFirstTimeConsuming)
            {
                System.out.println(name+" is initilizing...");
                Thread.sleep(4000);
                isFirstTimeConsuming=false;
            }
            try{

                if (queue.peek()==null)
                    Thread.currentThread().interrupt();

                Integer data = queue.take();

                System.out.println(name+" consumed ------->"+data);
                Thread.sleep(70);    

            }catch(InterruptedException ie)
            {
                System.out.println("InterruptedException!!!!");
                break;
            }
        }

        System.out.println("Comsumer " + this.name + " finished its job; terminating.");

    }catch (InterruptedException e)
    {
        e.printStackTrace();
    } 
}
公共类ConsumerThread实现可运行
{
私有阻塞队列;
私有字符串名称;
私有布尔值IsFirstTime=true;
公共使用者读取(字符串名称,阻塞队列)
{
this.queue=队列;
this.name=name;
}
@凌驾
公开募捐
{
尝试
{       
while(true)
{   
如果(IsFirstTime)
{
System.out.println(name+“正在初始化…”);
睡眠(4000);
IsFirstTime=false;
}
试一试{
if(queue.peek()==null)
Thread.currentThread().interrupt();
整数数据=queue.take();
System.out.println(name+“consumered-->”+数据);
睡眠(70);
}捕获(中断异常ie)
{
System.out.println(“InterruptedException!!!!”);
打破
}
}
System.out.println(“Comsumer”+this.name+“完成了它的工作;正在终止”);
}捕捉(中断异常e)
{
e、 printStackTrace();
} 
}

}不要在线程上使用中断,而是在不再需要时中断循环:

if (queue.peek()==null)
         break;
或者,您也可以使用变量标记关闭操作挂起,然后断开循环并在以下情况后关闭循环:

if (queue.peek()==null)
         closing = true;

//Do further operations ...
if(closing)
  break;

在现实世界中,大多数消息都带有某种类型的头,用于定义消息类型/子类型或可能不同的对象

您可以创建一个命令和控制对象或消息类型,告诉线程在收到消息时执行某些操作(如关闭、重新加载表、添加新侦听器等)


通过这种方式,您可以让一个命令和控制线程将消息发送到正常的消息流中。您可以让CNC线程与大型系统中的操作终端对话,等等。

如果您的队列可以在您希望消费者终止之前清空,那么您需要一个标志来告诉线程何时停止。添加一个setter方法,以便生产者可以告诉消费者关闭。然后修改代码,以代替:

if (queue.isEmpty())
   break;
检查代码

if (!run)
{
   break;
}
else if (queue.isEmpty())
{
   Thread.sleep(200);
   continue;
}

A:无法保证仅仅因为
peek
返回
null
,生产商就停止了生产。如果生产商只是放慢了速度怎么办?现在,消费者退出,生产商继续生产。因此,“peek”->“break”的想法基本上失败了

B:如果出现以下情况,则从使用者设置“完成/运行”标志并在生产者中读取也会失败:

  • 消费者检查标志,发现它应该继续运行,然后执行“take”
  • 与此同时,制作人正在将标志设置为“不运行”
  • 现在消费者永远在等待鬼包
  • 相反的情况也可能发生,一个数据包被忽略而未被使用

    然后,为了解决这个问题,您需要在“BlockingQueue”之上使用互斥体进行额外的同步

    C: 我发现“Rosetta代码”是决定什么是良好实践的良好来源,在以下情况下:

    生产者和消费者必须就表示输入结束的对象(或对象中的属性)达成一致。然后生产者在最后一个数据包中设置该属性,消费者停止消费该属性。即,您在问题中称之为“毒药”

    在上面的Rosetta代码示例中,此“对象”只是一个名为“EOF”的空字符串:

    final String EOF = new String();
    
    // Producer
    while ((line = br.readLine()) != null)
      queue.put(line);
    br.close();
    // signal end of input
    queue.put(EOF);
    
    // Consumer
    while (true)
      {
        try
          {
            String line = queue.take();
            // Reference equality
            if (line == EOF)
              break;
            System.out.println(line);
            linesWrote++;
          }
        catch (InterruptedException ie)
          {
          }
      }
    

    您可以将此类型安全模式用于毒药:

    public sealed interface BaseMessage {
    
        final class ValidMessage<T> implements BaseMessage {
    
            @Nonnull
            private final T value;
    
    
            public ValidMessage(@Nonnull T value) {
                this.value = value;
            }
    
            @Nonnull
            public T getValue() {
                return value;
            }
    
            @Override
            public boolean equals(Object o) {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                ValidMessage<?> that = (ValidMessage<?>) o;
                return value.equals(that.value);
            }
    
            @Override
            public int hashCode() {
                return Objects.hash(value);
            }
    
            @Override
            public String toString() {
                return "ValidMessage{value=%s}".formatted(value);
            }
        }
    
        final class PoisonedMessage implements BaseMessage {
    
            public static final PoisonedMessage INSTANCE = new PoisonedMessage();
    
    
            private PoisonedMessage() {
            }
    
            @Override
            public String toString() {
                return "PoisonedMessage{}";
            }
        }
    }
    
    public class Producer implements Callable<Void> {
    
        @Nonnull
        private final BlockingQueue<BaseMessage> messages;
    
        Producer(@Nonnull BlockingQueue<BaseMessage> messages) {
            this.messages = messages;
        }
    
        @Override
        public Void call() throws Exception {
            messages.put(new BaseMessage.ValidMessage<>(1));
            messages.put(new BaseMessage.ValidMessage<>(2));
            messages.put(new BaseMessage.ValidMessage<>(3));
            messages.put(BaseMessage.PoisonedMessage.INSTANCE);
            return null;
        }
    }
    
    public class Consumer implements Callable<Void> {
    
        @Nonnull
        private final BlockingQueue<BaseMessage> messages;
    
        private final int maxPoisons;
    
    
        public Consumer(@Nonnull BlockingQueue<BaseMessage> messages, int maxPoisons) {
            this.messages = messages;
            this.maxPoisons = maxPoisons;
        }
    
        @Override
        public Void call() throws Exception {
            int poisonsReceived = 0;
            while (poisonsReceived < maxPoisons && !Thread.currentThread().isInterrupted()) {
                BaseMessage message = messages.take();
                if (message instanceof BaseMessage.ValidMessage<?> vm) {
                    Integer value = (Integer) vm.getValue();
                    System.out.println(value);
                } else if (message instanceof BaseMessage.PoisonedMessage) {
                    ++poisonsReceived;
                } else {
                    throw new IllegalArgumentException("Invalid BaseMessage type: " + message);
                }
            }
            return null;
        }
    }
    
    公共密封接口基本消息{
    最后一个类ValidMessage实现BaseMessage{
    @非空
    私人最终T值;
    公共有效消息(@Nonnull T值){
    这个值=值;
    }
    @非空
    公共T getValue(){
    返回值;
    }
    @凌驾
    公共布尔等于(对象o){
    如果(this==o)返回true;
    如果(o==null | | getClass()!=o.getClass())返回false;
    ValidMessage that=(ValidMessage)o;
    返回值.equals(即.value);
    }
    @凌驾
    公共int hashCode(){
    返回Objects.hash(值);
    }
    @凌驾
    公共字符串toString(){
    返回“ValidMessage{value=%s}”。格式化(值);
    }
    }
    最后一个类TownedMessage实现BaseMessage{
    public static final-毒物消息实例=new-毒物消息();
    私人中毒信息(){
    }
    @凌驾
    公共字符串toString(){
    返回“中毒消息{}”;
    }
    }
    }
    公共类生产者实现了可调用{
    @非空
    私有最终阻塞队列消息;
    生产者(@Nonnull BlockingQueue消息){
    this.messages=消息;
    }
    @凌驾
    public Void call()引发异常{
    messages.put(新的BaseMessage.ValidMessage(1));
    messages.put(新的BaseMessage.ValidMessage(2));
    messages.put(新的BaseMessage.ValidMessage(3));
    messages.put(BaseMessage.毒物消息.INSTANCE);
    返回null;
    }
    }
    公共类使用者实现了可调用{
    @非空
    专用最终封锁队列