如何从 Swing worker 运行 服务执行器?
How to run service executor from Swing worker?
我正在编写一个 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);
}
}
运行 这段代码很好,但是当我将它附加到 开始扫描按钮 时,GUI 滞后,我用谷歌搜索并想出使用 Swing工人。当我单独实施 swing worker 时,它杀死了并发性,而当我同时实施这两个时,gui 仍然滞后。我的问题是无论如何都要让按钮(Swing worker)调用服务执行器来执行其他进程?
When i implemented the swing worker alone it killed the concurrency and when i implemented both the gui still lags.
这里有两件事要做:
将您的 ping 检查分布到多个线程
- 将您的任务拆分为独立的 sub-tasks
- 运行 sub-tasks 在 thread-pool
- 收集结果
从事件调度线程中分离整个操作
- 注册用户操作(点击、按键)、从文本字段获取数据、构建任务
- 运行 EDT 之外的任务
- 更新图形用户界面,显示结果
您正在使用 ExecutorService
为您的某些代码执行第一部分。第二部分未在您的代码中完成,因此 EDT 将阻塞直到整个操作完成,从而使您的图形界面滞后。
您需要将此代码移动到 swing 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
中。将其用于那些特殊情况可能是您的最佳选择。
我已经通过实现 swing worker 解决了我的问题,do 在后台 函数将为服务执行器启动一个新线程并防止延迟。
//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();
我正在编写一个 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);
}
}
运行 这段代码很好,但是当我将它附加到 开始扫描按钮 时,GUI 滞后,我用谷歌搜索并想出使用 Swing工人。当我单独实施 swing worker 时,它杀死了并发性,而当我同时实施这两个时,gui 仍然滞后。我的问题是无论如何都要让按钮(Swing worker)调用服务执行器来执行其他进程?
When i implemented the swing worker alone it killed the concurrency and when i implemented both the gui still lags.
这里有两件事要做:
将您的 ping 检查分布到多个线程
- 将您的任务拆分为独立的 sub-tasks
- 运行 sub-tasks 在 thread-pool
- 收集结果
从事件调度线程中分离整个操作
- 注册用户操作(点击、按键)、从文本字段获取数据、构建任务
- 运行 EDT 之外的任务
- 更新图形用户界面,显示结果
您正在使用 ExecutorService
为您的某些代码执行第一部分。第二部分未在您的代码中完成,因此 EDT 将阻塞直到整个操作完成,从而使您的图形界面滞后。
您需要将此代码移动到 swing 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
中。将其用于那些特殊情况可能是您的最佳选择。
我已经通过实现 swing worker 解决了我的问题,do 在后台 函数将为服务执行器启动一个新线程并防止延迟。
//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();