线程“Thread-0”中的异常java.util.NoSuchElementException?

时间:2015-12-07 09:12:50

标签: java multithreading list listiterator

我是新手使用线程。在另一个类中,创建并启动ConnectionMaster类的实例(扩展线程)。 Client对象被赋予ConnectionMaster对象,该对象将其添加到列表中。 Thread类的重写run()方法实质上是侦听要添加到列表的客户端。实际上,当客户端对象添加到列表中时,它会侦听并“听到”。但是,尽管.hasNext()返回true .Next()会导致异常。我做错了什么?

以下方法来自ConnectionMaster类,它扩展了Thread:

构造

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

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

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

这是Thread类的重写线程方法。它始终检查添加到列表中的元素。

@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();
        }
    }
}

//////////////////////////////// UPDATE ////////////// ////////////////////// 谢谢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();
        }
    }
}

2 个答案:

答案 0 :(得分:1)

这是实现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();
    }

}

答案 1 :(得分:0)

  

我做错了什么?

实际上很多。

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

第二件事是,即使迭代器/列表迭代器是线程安全的,你也在执行一系列操作(例如VectorhasNextnext)而不做任何事情确保以线程安全的方式执行操作的序列。两个线程可能在共享迭代器上同时执行相同的序列,而另一个线程可能会干扰另一个线程。

我不确定要修改代码的建议。共享迭代器的两个线程不起作用。

放弃它可能会更好,并按照@OldCurmugeon的建议使用某种remove

问题1或问题2(如上所述)可能会导致Queue例外。

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