Java NIO仍在阻止GUI

问题描述 投票:0回答:2

编辑:现在会弹出GUI(感谢matt,但是当我按下开始按钮时,程序完全冻结,我必须在jGrasp中结束它。

我在使用Java NIO时遇到问题,当我运行服务器代码时,我的GUI不会弹出。

这里是代码:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.event.*;

public class Server extends JFrame implements ActionListener{

  JButton start = null;

  public Server(){
     JPanel panel = new JPanel(new FlowLayout());
     start = new JButton("Start");
     add(panel);
     panel.add(start);
     start.addActionListener(this);
  }

  public void start(){
     try{
        Selector selector = Selector.open();

        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);

        InetSocketAddress hostAddress = new InetSocketAddress("localhost", 0);
        serverChannel.bind(hostAddress);

        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
           int readyCount = selector.select();
           if (readyCount == 0) {
              continue;
           }
        // process selected keys...
           Set<SelectionKey> readyKeys = selector.selectedKeys();
           Iterator<SelectionKey> iterator = readyKeys.iterator();
           while (iterator.hasNext()) {
              SelectionKey key = iterator.next();
           // Remove key from set so we don't process it twice
              iterator.remove();
           // operate on the channel...
           // client requires a connection
              if (key.isAcceptable()) {
                 ServerSocketChannel server = (ServerSocketChannel)  key.channel();    
              // get client socket channel
                 SocketChannel client = server.accept();
              // Non Blocking I/O
                 client.configureBlocking(false);
              // record it for read/write operations (Here we have used it for read)
                 client.register(selector, SelectionKey.OP_READ);
                 continue;
              }
           }
        }
     }
     catch(IOException ioe){}
  }

  public void actionPerformed(ActionEvent e) {    
     if(e.getSource()==start){
        start();
     }
  }

  public static void main(String []args){
     Server gui = new Server();
     gui.setTitle("Server");
     gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     gui.pack();
     gui.setLocationRelativeTo(null);
     gui.setResizable(false);
     gui.setVisible(true);
  }
} 

我在这里做错了什么?我遵循了this教程,并在进行简单调试(Iterator<SelectionKey> iterator = readyKeys.iterator();缺少<SelectionKey>部分)后,我进行了编译,运行和任何操作。这是我编写的全部代码,我不明白自己在做什么错。

java user-interface nio
2个回答
0
投票

您需要分开您的任务。一种是启动gui,另一种是启动服务器。

使一项任务成为服务器。

public void runServer() throws Exception{
        Selector selector = Selector.open();
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);

        InetSocketAddress hostAddress = new InetSocketAddress("localhost", 0);
        serverChannel.bind(hostAddress);

        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
           int readyCount = selector.select();
           if (readyCount == 0) {
              continue;
           }
           Set<SelectionKey> readyKeys = selector.selectedKeys();
           Iterator<SelectionKey> iterator = readyKeys.iterator();
           while (iterator.hasNext()) {
              SelectionKey key = iterator.next();
              iterator.remove();
              if (key.isAcceptable()) {
                 ServerSocketChannel server = (ServerSocketChannel)  key.channel();    
              // get client socket channel
                 SocketChannel client = server.accept();
              // Non Blocking I/O
                 client.configureBlocking(false);
              // record it for read/write operations (Here we have used it for read)
                 client.register(selector, SelectionKey.OP_READ);
                 continue;
              }
           }
        }
     }

}

然后将其他任务设置为gui。

public void startGui(){
    JPanel panel = new JPanel(new FlowLayout());
    JButton start = new JButton("Start");
    add(panel);
    panel.add(start);
    setTitle("Server");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    pack();
    setLocationRelativeTo(null);
    setResizable(false);
    setVisible(true);
}

现在您的主要方法可以简化为。

public static void main(String[] args) throws Exception{
    Server server = new Server();
    EventQueue.invokeAndWait( server::startGui );
    server.runServer();
}

这样,gui在EDT上启动,而永不结束的服务器循环占用了主线程。另一个小变化。不要扩展JFrame,只需在startGui方法中创建一个JFrame。这样,[[all gui工作就在EDT上完成。

不是,我也删除了您的异常处理,只是使方法抛出异常。这样,您将看到StackTrace。

关于您的新问题,为什么GUI被冻结。这是因为您阻止了EDT。您的start()方法永远不会退出。解决此问题的最简单的方法是。

public void actionPerformed(ActionEvent e) { if(e.getSource()==start){ new Thread( ()->start();).start(); }

}

这将启动一个新线程来运行您的服务器。注意最明显的问题,如果您单击“启动”多次,它将启动多个线程!


0
投票
在这种情况下,可能不需要使用非阻塞I / O。相反,主线程可以继续执行I / O(使用传统的阻塞调用),同时使用SwingWorker调度对UI的更新。这些更新将在Swing事件分配线程中进行,因此您甚至不必担心创建自己的线程。

非阻塞IO,您可以检查通道是否准备好执行某些操作而不会阻塞。如果尚未准备好I / O操作,则线程可以执行其他任务,并稍后再检查。

但是在您的程序中,您编写的代码有效地等待(处于“忙循环”中)操作准备就绪。因为构造函数永远不会返回,所以甚至不会调用setVisible()。您永远不会给线程一个机会做任何其他工作,包括绘制UI。

此外,对于Java Swing,有一个特殊的线程对UI执行所有更新。当您创建组件或更新它们显示的内容时,必须使用该线程来完成该工作。 Swing提供SwingWorker类来安排可操作UI的简短任务。您可以通过完成SwingWorker

来了解有关Swing中的并发性的更多信息。
© www.soinside.com 2019 - 2024. All rights reserved.