【问题标题】:Java thread producer and consumer program issueJava线程生产者和消费者程序问题
【发布时间】:2016-11-25 07:32:25
【问题描述】:

我正在尝试 Java 线程生产者和消费者程序。 但是消费者线程总是进入等待状态。

我无法调试为什么消费者线程总是进入等待状态或生产者不通知消费者线程的问题

请帮我解决这个问题。程序如下。

通讯器类同时调用生产者和消费者类

public class Communicator {

   Thread t = null;
    Thread t1 = null;

    public void runThread() {
        Producer p = new Producer();
        Consumer c = new Consumer(p);
        t = new Thread(p);
        t1 = new Thread(c);
        t.start();
        t1.start();
        Thread tr = new Thread() {
            public void run() {
                for (int i = 0; i < 30; i++) {
                    System.out.println("t::::::::::::: " + t.getState());
                    System.out.println("t1::::::::::::: " + t1.getState());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                }
            }
        };
        tr.start();
    }

    public static void main(String[] args) {
        Communicator c = new Communicator();
        c.runThread();
    }
}

这是生产者类,将数据附加到字符串缓冲区并通知消费者类

public class Producer extends Thread {
        public StringBuffer sb;

        public Producer() {
            sb = new StringBuffer();
        }

        public void run() {
            synchronized (sb) {
                try {
                    System.out.println("Bala");
                    sb.append("murugan");
                    sb.notify();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

下面是消费类代码。它等待从生产者类获取通知。

public class Consumer extends Thread {
    public Producer p;

    public Consumer(Producer p) {
        this.p = p;

    }

    public void run(){
        synchronized (p.sb) {
            try {

                p.sb.wait();

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(p.sb);
        }
    }


}

【问题讨论】:

  • 在进入Consumer 类中的wait() 之前,您应该执行业务逻辑来处理字符串缓冲区中的内容。仅当没有可处理的数据时,您才应该使用wait()
  • 这个问题可能有用:stackoverflow.com/questions/37683895/…。它使用 BlokcingQueues 删除等待和通知。用字符串替换整数
  • @user3509105 请参考下面我的回答。希望对您有所帮助。方法notify() 仅唤醒当前未唤醒但正在等待通知的“合格线程”,以便他们可以获取锁。希望对您有所帮助。

标签: java multithreading synchronization thread-sleep java-threads


【解决方案1】:

您当前的代码存在一些问题,其中消费者线程始终处于等待状态,而生产者已经终止。

另外,您的 StringBuffer 对象必须是 volatile,以便生产者线程写入的内容将被刷新并可供其他线程使用。

除此之外,我还修改了您的 ProducerConsumer 代码,使其更加真实(两者都在 while 循环中运行,一个产生一些数据,另一个接收数据)如下所示:(我有还添加了 1 秒的睡眠时间以更慢的速度运行这些东西,以便您更好地理解这些东西):

消费类:

public class Producer extends Thread {
        public volatile StringBuffer sb;

        public Producer() {
            sb = new StringBuffer();
            sb.append("");
        }

        public void run() {
            synchronized (sb) {
                try {
                    while(true) {
                        Thread.sleep(1000);
                        if(sb.toString().equals("")) {
                            sb.append("murugan");
                            System.out.println(" producing sb completed *** ");
                            sb.notify();
                        } else {
                            sb.wait();
                        }
                    } 
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

消费类:

public class Consumer extends Thread {
        public Producer p;

        public Consumer(Producer p) {
            this.p = p;

        }

        public void run(){
            synchronized (p.sb) {
                try {
                    while(true) {
                        Thread.sleep(1000);
                        if(p.sb.toString().equals("")) {
                          p.sb.wait();
                        } else {
                            String str = p.sb.toString();
                            System.out.println(" consuming sb completed **** "+str);
                            p.sb.replace(0, str.length(), "");
                            p.sb.notify();
                        }
                    }
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(p.sb);
            }
        }
    }

【讨论】:

    【解决方案2】:

    对于您的问题,“我无法调试消费者线程总是进入等待状态或生产者未通知消费者线程的问题”。 实际上,您的消费者并不总是处于等待状态。 您可以将 Thread.sleep(1000); 放在 p.sb.wait(); 之前在您的 Consumer 类中,您可以看到一次“consumerThread:::::::::::::: RUNNABLE”。 恕我直言,您的消费者代码运行速度太快而无法获得等待状态,因此您错过了可运行状态。您可以从其他答案中了解更多信息。

    【讨论】:

      【解决方案3】:

      Producer 已经终止,并且在 Consumer 调用 wait() 之前已经调用了 notify()

      由于ProducerConsumer extends Thread,将Communicator 类更新为:

      public class Communicator {
          public void runThread() {
              final Producer p = new Producer();
              final Consumer c = new Consumer(p);
      
              p.start();
              c.start();
      
              Thread tr = new Thread() {
                  public void run() {
                      for (int i = 0; i < 30; i++) {
                          System.out.println("t::::::::::::: " + p.getState());
                          System.out.println("t1::::::::::::: " + c.getState());
                          try {
                              Thread.sleep(2000);
                          } catch (InterruptedException ie) {
                              ie.printStackTrace();
                          }
                      }
                  }
              };
              tr.start();
          }
      
          public static void main(String[] args) {
              Communicator c = new Communicator();
              c.runThread();
          }
      }
      

      如果Producer 尚未终止[if (p.getState() != Thread.State.TERMINATED)],那是Consumer 唯一等待的时间:

      public class Consumer extends Thread {
          public Producer p;
      
          public Consumer(Producer p) {
              this.p = p;
      
          }
      
          public void run() {
              synchronized (p.sb) {
                  try {
      
                      if (p.getState() != Thread.State.TERMINATED) {
                          p.sb.wait();
                      }
      
                  } catch (InterruptedException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
                  System.out.println(p.sb);
              }
      
          }
      }
      

      【讨论】:

        【解决方案4】:

        这不是一个答案,而是一个建议……您可以使用 BlockingQueue 简化整个逻辑,将数据从 Producer(s) 传输到 Consumer(s)。所有等待和通知都会消失!

        Producer(s) send data to be consumed calling BlockingQueue.offer(String)
        
        Consumer(s) wait (blocked) for data calling BlockingQueue.pool();
        

        【讨论】:

          【解决方案5】:

          根据您的代码,Consumer Thread 等待Producernotify 关于附加在StringBuffer 中的字符串。

          1. 如果Producer 线程有机会获得shared StringBuffer object 上的锁(它进入synchronized block)那么Consumer Thread 将进入Blocked state不会能够输入synchronized block)作为它也是锁的竞争者(两者都竞争获取同一共享对象上的锁)。
          2. 生产者线程完成其执行,离开synchronized block 并被终止。 请注意,通知代码不会产生任何影响,因为消费者线程尚未等待共享对象,因为它尚未进入同步块
          3. Consumer thread 有机会获得lock 并输入synchronized blockwaits 让某人在共享对象上发出通知。但由于 Producer 已经终止,没有人向Consumer thread 发出通知,它仍处于Waiting 状态。

          修复:在您的情况下,您可以简单地确保首先启动 Consumer thread 并在生产者线程之前获取锁。为此,您可以在启动消费者线程后让主线程休眠一段时间。

          t = new Thread(p);
          t1 = new Thread(c);
          t1.start();
          try {
                  Thread.sleep(1000);
              }catch (InterruptedException e) {
                  e.printStackTrace();
              }
          t.start();
          

          关键点:如果你只有2个线程,一个线程应该调用notifywait。其他线程收到通知后,只有竞争 Lock 的线程才能获取锁并完成其工作。完成其工作后,它应该调用通知并等待另一个线程完成工作并在完成后发出通知。这样,两个线程都有机会一个接一个地完成它们的工作。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-04-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-04-12
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多