Java NoSuchElementException 异常

Java NoSuchElementException odity

我们遇到了这个异常,考虑到我们的代码,这似乎是不可能的... 我们将项目添加到静态链表中,然后启动一个线程来处理添加到这些列表中的项目。如果在将项目添加到列表时静态线程已死或为空,我们将再次启动它。一旦处理完所有项目,该线程就会使自己为空。

请参阅下面的小片段

代码:

public class LiveKPIUpdates extends Thread {

  private static final int MAX_QUEUE_SIZE = 1000;
  private static LinkedList < UpdateLine > highQueue = new LinkedList < UpdateLine > ();
  private static LinkedList < UpdateLine > lowQueue = new LinkedList < UpdateLine > ();
  private static UpdateThread updateThread = null;

  /**
   * Thread to process live update queues.
   * High priority items are processed first.
   */
  static class UpdateThread extends Thread {
    @Override
    public void run() {
      while (!highQueue.isEmpty() || !lowQueue.isEmpty()) {
        UpdateLine update = !highQueue.isEmpty() ? highQueue.removeFirst() : lowQueue.removeFirst(); // <<<<< !!! EXCEPTION HAPPENS HERE... Why/How? !!! >>>>>>>>>>>>>>>
        //Do stuff with update        
      }
      updateThread = null;
    }
  }

  /**
   * Adds the live update to a queue to be processed.
   * If the queue is full, the update may be ignored.
   *
   * @param appender The machine who sent these lines
   * @param lines The lines the machine sent. (Can be tray file or machine log lines)
   */
  synchronized public static void updateKPIWithMachineLines(AppenderID appender,
    String fileName, Date fileDate, LinkedList < String > lines) throws ParseException, SQLException {
    for (String line: lines) {
      if (line.startsWith("something...")) {
        if (highQueue.size() < MAX_QUEUE_SIZE) {
          highQueue.add(new UpdateLine(appender, fileName, fileDate, line));
        } else {
          TLSystemAction.log("Live updates high priority queue full", appender);
        }
      } else {
        if (lowQueue.size() < MAX_QUEUE_SIZE) {
          lowQueue.add(new UpdateLine(appender, fileName, fileDate, line));
        } else {
          TLSystemAction.log("Live updates low priority queue full", appender);
        }
      }
    }

    if (updateThread == null) {
      updateThread = new UpdateThread();
      updateThread.start();
    } else if (!updateThread.isAlive()) {
      updateThread.interrupt();
      updateThread = new UpdateThread();
      updateThread.start();
    }
  }

异常:

[#|2015-03-12T11:12:10.614+1300|SEVERE|glassfish3.0.1|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=617736;_ThreadName=Thread-1;|java.util.NoSuchElementException
at java.util.LinkedList.remove(LinkedList.java:788)
at java.util.LinkedList.removeFirst(LinkedList.java:134)
at api.tl.put.LiveKPIUpdates$UpdateThread.run(LiveKPIUpdates.java:67)

编辑: 我更感兴趣的是如果 lowQueue 为空,它是如何到达 lowQueue.removeFirst() 的。逻辑似乎可以避免这种情况。

改变这个

while (!highQueue.isEmpty() || !lowQueue.isEmpty()) {
    UpdateLine update = !highQueue.isEmpty() ? highQueue.removeFirst() : lowQueue.removeFirst(); // <<<<< !!! EXCEPTION HAPPENS HERE... Why/How? !!! >>>>>>>>>>>>>>>
    //Do stuff with update        
  }

至此

while (!highQueue.isEmpty() || !lowQueue.isEmpty()) {
    UpdateLine update = !highQueue.isEmpty() ? highQueue.removeFirst() : (!lowQueue.isEmpty() : lowQueue.removeFirst() ? [something else that you know]); // <<<<< !!! EXCEPTION HAPPENS HERE... Why/How? !!! >>>>>>>>>>>>>>>
    //Do stuff with update        
  }

基本上,根据您调用 lowQueue.removeFirst() 的代码,您的 lowQueue 可以为空,因为 ||如果第一个条件为真,则 while() 中的运算符通过,并且在这种情况下它不检查第二个条件,因为它是一个或操作。

你可以查看这个

Does Java evaluate remaining conditions after boolean result is known?

或这个

http://en.wikipedia.org/wiki/Short-circuit_evaluation

如果您在代码中列出空条件,那么看起来不可能出现这种异常。看起来 @muasif80 的回答也可能会解决它。但是,由于竞争条件,您的代码中可能会出现意外行为。您只看到一种问题;在现实生活中,您可能会看到更多此类意外和无法解释的故障。 java.util.LinkedList 不是线程安全的 class。当您在多个线程之间共享线程不安全 class 的实例时,您会看到这种意想不到的行为。同步列表访问,您应该可以毫无问题地看到代码 运行。由于您有多个列表,因此您应该在一个公共对象上进行同步。

public class LiveKPIUpdates extends Thread {
  private static final int MAX_QUEUE_SIZE = 1000;
  private static LinkedList < UpdateLine > highQueue = new LinkedList < UpdateLine > ();
  private static LinkedList < UpdateLine > lowQueue = new LinkedList < UpdateLine > ();
  private static UpdateThread updateThread = null;
  private static Object lock = new Object();

  /**
   * Thread to process live update queues.
   * High priority items are processed first.
   */
  static class UpdateThread extends Thread {
    @Override
    public void run() {
      boolean queueHasData = false;
      synchronized(lock) {
        queueHasData = !highQueue.isEmpty() || !lowQueue.isEmpty();
      }
      while (queueHasData) {
        UpdateLine update;
        synchronized(lock) {
          update = !highQueue.isEmpty() ? highQueue.removeFirst() : lowQueue.removeFirst();
          queueHasData = !highQueue.isEmpty() || !lowQueue.isEmpty();
        }
        //Do stuff with update        
      }
      updateThread = null;
    }
  }

  /**
   * Adds the live update to a queue to be processed.
   * If the queue is full, the update may be ignored.
   *
   * @param appender The machine who sent these lines
   * @param lines The lines the machine sent. (Can be tray file or machine log lines)
   */
  synchronized public static void updateKPIWithMachineLines(AppenderID appender,
    String fileName, Date fileDate, LinkedList < String > lines) throws ParseException, SQLException {
    for (String line: lines) {
      if (line.startsWith("something...")) {
        synchronized(lock) {
          if (highQueue.size() < MAX_QUEUE_SIZE) {
            highQueue.add(new UpdateLine(appender, fileName, fileDate, line));
          } else {
            TLSystemAction.log("Live updates high priority queue full", appender);
          }
        }
      } else {
        synchronized(lock) {
          if (lowQueue.size() < MAX_QUEUE_SIZE) {
            lowQueue.add(new UpdateLine(appender, fileName, fileDate, line));
          } else {
            TLSystemAction.log("Live updates low priority queue full", appender);
          }
        }
      }
    }

    if (updateThread == null) {
      updateThread = new UpdateThread();
      updateThread.start();
    } else if (!updateThread.isAlive()) {
      updateThread.interrupt();
      updateThread = new UpdateThread();
      updateThread.start();
    }
  }

虽然此代码在同步访问列表后可能会按预期工作,但这不是最佳解决方案。这是一个 class 典型的生产者-消费者问题。 Java 具有用于处理生产者消费者问题的等待...通知机制。您可以在网络上获得许多生产者消费者的示例。阅读、学习并实施等待...通知模式。