java - SelectionKey iterator.remove() 抛出 UnsupportedOperationException 和无限循环

标签 java json gson nio socketchannel

我有一个方法可以打开连接、查询站点、获取页面数,然后使用 NIO 并发检索所有页面。第一个查询是使用 URLConnection 完成的,并且工作得很好。当我尝试使用 NIO 选择器和 channel 时遇到两个问题:

1) 如果我不从迭代器中删除键,则会在无限循环中运行打印 size() 并发送查询。如果我尝试删除 key ,则会收到 UnsupportedOperationsException。呸!

2) 在写入套接字后,是否需要从 OP_WRITE 取消注册 channel ?如果是这样,我可以直接调用channel.register(selector, SelectionKey.OP_READ)来消除写入的兴趣吗?

public void test() throws IOException {
// create selector
Selector selector = Selector.open();
System.out.println("opened");

// get the number of pages
URL itemUrl = new URL(ITEM_URL);
URLConnection conn = itemUrl.openConnection();
conn.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
// out.write(getHeaderString(itemUrl));
out.write(new Query("", "Internal Hard Drives", false, false, true, false, -1, 7603, 1, 14, -1, "", "PRICE", 1).toString());
out.close();

JsonReader in = new JsonReader(new InputStreamReader(conn.getInputStream()));
JsonParser parser = new JsonParser();
JsonObject tempObj = (JsonObject) parser.parse(in);
Pages.setNumOfPages(getNumberOfIterations(tempObj.get("PaginationInfo")));
System.out.println("Pages: " + Pages.getNumOfPages());

    // for each page, create a channel, attach to selector with interest in read
    // typically this would be i <= Pages.getNumberOfPages but to troubleshoot, i'm limiting this to just once.
for (int i = 1; i <= 1; i++) {
    SocketChannel channel = SocketChannel.open();
    channel.configureBlocking(false);
    channel.connect(new InetSocketAddress(itemUrl.getHost(), 80));
    channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
}
selector.select();
Set<SelectionKey> sk = selector.keys();

while (!sk.isEmpty()) {
    System.out.println(sk.size());
    Iterator<SelectionKey> iterator = sk.iterator();
    while (iterator.hasNext()) {
    SelectionKey key = iterator.next();

            iterator.remove();
    if (key.isReadable()) {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buf = ByteBuffer.allocate(8192);
        channel.read(buf);
        buf.flip();
        Product p = parse(buf, Product.class);
        if (p != null) {
        finalItems.add(p);
        System.out.println("Item added!");
        key.cancel();
        }
    } else if (key.isWritable()) {
        SocketChannel channel = (SocketChannel) key.channel();
        System.out.println(itemUrl);
        System.out.println(new Query("", "Internal Hard Drives", false, false, true,
            false, -1, 7603, 1, 14, -1, "", "PRICE", 1).toString());
        channel.write(ByteBuffer.wrap(new Query("", "Internal Hard Drives", false,
            false, true, false, -1, 7603, 1, 14, -1, "", "PRICE", 1).toString()
            .getBytes()));

    }
    }
    selector.select();
    sk = selector.keys();
}
}

最佳答案

来自http://docs.oracle.com/javase/6/docs/api/java/nio/channels/Selector.html#keys()

“ key 集不可直接修改。 key 只有在取消且其 channel 已注销后才会被删除。任何修改 key 集的尝试都会导致抛出 UnsupportedOperationException。”

您想要使用 Selector.selectedKeys();

“键可以从选定的键集中删除,但不能直接添加到选定的键集中。任何将对象添加到键集中的尝试都将导致抛出 UnsupportedOperationException。”

selector.select();
Set<SelectionKey> sk = selector.selectedKeys();

然后你可以使用Iterator.remove()

这里发布了一个很好的例子http://tutorials.jenkov.com/java-nio/selectors.html在页面底部

关于java - SelectionKey iterator.remove() 抛出 UnsupportedOperationException 和无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16553510/

相关文章:

java - 迭代 JSON 并将列表数组添加到对象/数组中

mysql - 从 MySQL JSON 数组中获取不同的值

python - 删除括号之间的所有最后一个逗号

android - Retrofit2标量转换器在gson转换器android之前不转换

java - Google 服务应用插件错误,无法解析 com.google.android.gms.11.0.4 和错误 :(45, 0)

json - 将对象传递给 DataSnap Server 方法?

java - 如何使用Gson解析以下json?

java - 从现有的 java gui 程序在新终端中打开 jar 文件

java - JVM 上具有多个线程的进程是否会比具有一个线程的进程拥有更多的 cpu 时间?

java - Spring 中的命令对象