线程 "Thread-0" java.util.NoSuchElementException 中出现异常?

Exception in thread "Thread-0" java.util.NoSuchElementException?

我是线程的新手。在另一个 class 中创建并启动了 ConnectionMaster class 的实例(扩展线程)。将 Client 对象提供给 ConnectionMaster 对象,后者将其添加到列表中。 Thread class 的重写 运行() 方法本质上是监听要添加到列表中的客户端。实际上,当将 Client 对象添加到列表时,它确实会监听并 "hears" 。但是,尽管 .hasNext() returns true .Next() 会导致异常。我做错了什么?

以下方法来自 class ConnectionMaster 扩展 Thread:

构造函数

public ConnectionMaster(){
    clients = new Vector<>();
    listIterator = clients.listIterator();
}

Public 将客户端对象添加到列表的方法

@Override
public synchronized void addClient(Client client) {
    listIterator.add(client);
}

这是 class 线程的覆盖线程方法。它始终检查添加到列表中的元素。

@Override
public void run(){
    while(true){
        while(listIterator.hasNext()){
            processClient(listIterator.next()); //this is where error occurs
            listIterator.remove();
        }

        while(listIterator.hasPrevious()){
            processClient(listIterator.previous());
            listIterator.remove();
        }
    }
}

/////////////////////////////更新//////////// //////////////////////// 谢谢 OldCurmudgeon 和 Stephen C. 根据您的反馈,我的代码已修改为:

构造函数

public ConnectionMaster(){
    clients = new ArrayBlockingQueue<Client>(1024);
}

接收客户端对象的方法

@Override
public synchronized void addClient(Client client) {
    try {
        clients.put(client);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

听众

@Override
public void run(){
    while(true){
        try {
            processClient((Client)clients.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这是一种非常奇怪的实现方式Producer/Consumer。通常的方法是使用 BlockingQueue.

public class TwoThreads {

    public static void main(String args[]) throws InterruptedException {
        System.out.println("TwoThreads:Test");
        new TwoThreads().test();
    }

    // The end of the list.
    private static final Integer End = -1;

    static class Producer implements Runnable {

        final BlockingQueue<Integer> queue;

        public Producer(BlockingQueue<Integer> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 1000; i++) {
                    queue.add(i);
                    Thread.sleep(1);
                }
                // Finish the queue.
                queue.add(End);
            } catch (InterruptedException ex) {
                // Just exit.
            }
        }

    }

    static class Consumer implements Runnable {

        final BlockingQueue<Integer> queue;

        public Consumer(BlockingQueue<Integer> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            boolean ended = false;
            while (!ended) {
                try {
                    Integer i = queue.take();
                    ended = i == End;
                    System.out.println(i);
                } catch (InterruptedException ex) {
                    ended = true;
                }
            }
        }

    }

    public void test() throws InterruptedException {
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
        Thread pt = new Thread(new Producer(queue));
        Thread ct = new Thread(new Consumer(queue));
        // Start it all going.
        pt.start();
        ct.start();
        // Wait for it to finish.
        pt.join();
        ct.join();
    }

}

What am I doing wrong?

实际上很多。

您做错的第一件事是(显然)在多个线程中使用 ListIterator 对象。 VectorListIteratorIterator 实现不是线程安全的1,因此您正在做的事情有潜在危险。

第二件事是,即使迭代器/列表迭代器是线程安全的,您正在执行一系列操作(例如 hasNextnextremove)而不做任何事情来确保 sequence 操作以线程安全的方式执行。两个线程可能同时在共享迭代器上执行相同的序列,并且一个线程可能会干扰另一个线程,这是一种明显的可能性。

我不确定要修复您的代码有什么建议。两个线程共享一个迭代器是行不通的。

最好放弃它,并按照@OldCurmugeon 的建议使用某种 Queue


问题 1 或问题 2(如上所述)都可能导致 NoSuchElement 异常。


1 - 通过检查源代码可以明显看出这一点 - http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/Vector.java#Vector.ListItr