如何从Swing worker运行服务执行程序?

时间:2017-02-14 14:21:27

标签: java swing concurrency swingworker

我正在编写一个IP扫描程序应用程序,这个过程需要很长时间,因此我在 gui 的后台使用的是服务执行程序,如:

 public static List<Future<String>> checkThisIP(String ipStart, String ipEnd) throws UnknownHostException {
    final ExecutorService es = Executors.newFixedThreadPool(10);
    final List<Future<String>> futures = new ArrayList<>();
    String ipStringStart;
    String ipStringEnd;
    String targetIpString;
    //my update
    ipStringStart = ipStart.substring(ipStart.lastIndexOf(".") + 1, ipStart.length());
    ipStringEnd = ipEnd.substring(ipEnd.lastIndexOf(".") + 1, ipEnd.length());
    targetIpString = ipStart.substring(0, ipStart.lastIndexOf(".") + 1);
    if (!ipStart.equals(ipEnd)) {
        for (int i = Integer.parseInt(ipStringStart); i <= Integer.parseInt(ipStringEnd); i++) {
            String currentIp = targetIpString + i;
            futures.add(runPingScan(es, currentIp));
        }
    } else {
        futures.add(runPingScan(es, ipStart));
    }
    es.shutdown();
    return futures;
}


public static Future<String> runPingScan(final ExecutorService es, final String ip) {
    return es.submit(new Callable<String>() {
        @Override
        public String call() {
            String returnMe = "";
           //custom ping class 
            Ping p = new Ping();
            //send message
            p.SendReply(ip);
            //IsReachable returns ture or false
            if(p.IsReachable()){
                returnMe=ip;
            }
            return returnMe;
           }

    });
}

这是使用Jbutton执行的原始滞后代码操作:

 // scan result is Future list returned from service executor 
    List<Future<String>> scanResult = p.checkThisIP(jFormattedTextField1.getText(), jFormattedTextField2.getText());
                    for (final Future<String> f : scanResult) {
                        try {
                            ip = f.get();
                            if (!ip.equals("")) {
                                arp ARP = new arp();
                                PortScan openPort = new PortScan();
                                IP ipClass = new IP();
                                mac = ARP.getMac(ip);
                                manufacturer = ARP.getOUI(mac);
                                ports = openPort.checkIpForPorts(ip);
                                hostname = ipClass.hostname(ip);
                                title = ipClass.htmlTitle(ip);
                                Object[] data = {ip, mac, manufacturer, ports, hostname, title};
                                tableModel.addRow(data);
                            }

                            if (jFormattedTextField1.getText().equals(jFormattedTextField2.getText()) && ip.equals("")) {
                                JOptionPane.showMessageDialog(null, "<html>Can not ping the address ! <br> Server might be protected by <b>WAF</b>.</html>", "Alert", HEIGHT);
                            }
                        } catch (Exception ex) {
                            Logger.getLogger(gui.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }

运行此代码很好但是当我将它附加到开始扫描按钮时,我用谷歌搜索并想出使用 Swing Worker 。当我单独实现swing工作程序时它杀死了并发性,当我实现两个gui仍然滞后。我的问题是,无论如何使按钮(Swing worker)调用服务执行器来执行其他过程?

2 个答案:

答案 0 :(得分:1)

  

当我单独实施swing工作时,它会杀死并发性,当我实现两个gui仍然滞后时。

这里有两件事要做:

  • 在多个线程上传播ping检查

    • 将您的任务拆分为独立的子任务
    • 在线程池中运行子任务
    • 收集结果
  • 从事件分配线程中分离整个操作

    • 注册用户操作(单击,按键),从文本字段获取数据,构建任务
    • 在EDT之外运行任务
    • 更新gui,显示结果

对于某些代码,您正在使用ExecutorService执行第一部分。第二部分没有在你的代码中完成,所以EDT将阻塞直到整个操作完成,使你的gui滞后。

您需要将此代码移动到swing worker,该worker在执行程序中运行任务:

List<Future<String>> scanResult = p.checkThisIP(jFormattedTextField1.getText(), jFormattedTextField2.getText());
                for (final Future<String> f : scanResult) {
                    try {
                        [...] // this is where the thread blocks, making your ui lag if it's the EDT
                        Object[] data = {ip, mac, manufacturer, ports, hostname, title};

首先,移动所有阻塞代码由执行程序的线程池处理:

public static Future<Object[]> runPingScan(final ExecutorService es, final String ip) {
    return es.submit(new Callable<Object[]>() {
        @Override
        public Object[] call() {
            //custom ping class 
            Ping p = new Ping();
            //send message
            p.SendReply(ip);
            //IsReachable returns ture or false
            if(p.IsReachable()){
                [...] // other blocking code
                return {ip, mac, manufacturer, ports, hostname, title};
            } else {
                // special case, use null values or throw an exception
            }
        }
    });
}

然后你可以使用Simple Background Tasks教程代码将整个东西从EDT中分离出来:

SwingWorker worker = new SwingWorker<List<Object[]>, Void>() {
    public List<Object[]> doInBackground() {
        // -- this will run in another thread --
        // submit ping checks to the executor
        List<Future<Object[]>> scanResult = [...]
        // get results, put them in a list, return it
        List<Object[]> result = new ArrayList<>();
        for(Future<Object[]> f : scanResult) {
            result.add(f.get()); // blocking happens here, outside of the EDT
        }
        return result;
    }

    public void done() {
        // -- this will run in the EDT --
        // get() the list created above
        // display the result in the gui
        for(Object[] data : get()) {
            tableModel.addRow(data);
        }
    }
};

这里没有包含特殊情况,例如ping检查失败,你需要以某种方式处理它们。调用f.get()时,在ExecutionException中包含的每个异常都会被重新引发。对那些特殊情况使用它可能是你最好的选择。

答案 1 :(得分:1)

我已经设法通过实现swing工作器来解决我的问题,并且函数在后台执行将为服务执行程序启动一个新线程并防止延迟。

//The actionpreformed by the button
   SwingWorker worker = new SwingWorker<Void, Void>() {
            @Override
        // All actions are done this method
            protected Void doInBackground() throws Exception {
                String ip = "";
                String mac = "";
                String manufacturer = "";
                String ports = "";
                String hostname = "";
                String title = "";
                tableModel.setRowCount(0);
                PingScan p = new PingScan();
                List<Future<String>> scanResult = p.checkThisIP(jFormattedTextField1.getText(), jFormattedTextField2.getText());
                for (final Future<String> f : scanResult) {
                    try {
                        ip = f.get();
                        if (!ip.equals("")) {
                            arp ARP = new arp();
                            PortScan openPort = new PortScan();
                            IP ipClass = new IP();
                            mac = ARP.getMac(ip);
                            manufacturer = ARP.getOUI(mac);
                            ports = openPort.checkIpForPorts(ip);
                            hostname = ipClass.hostname(ip);
                            title = ipClass.htmlTitle(ip);
                            Object[] data = {ip, mac, manufacturer, ports, hostname, title};
                            tableModel.addRow(data);
                        }
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                }
                return null;
            }
        };
     worker.execute();