我遇到了为我的 gui 创建停止/启动按钮的问题,经过大量的谷歌搜索,我意识到我需要多线程。在进一步阅读后,我发现了 swingworker 类,我设法让我的 GUI 响应 STOP 按钮。
现在我的问题是这个
doinbackground(( 方法执行一段代码,该代码在无限的 while 循环中捕获带有条件 (!isCancel( 的数据包,一旦它被取消(STOP 按钮执行 worker.cancel(((,它就会返回一个数据包的 ArrayList,理论上,我应该能够在 done(( 方法中使用 get(( 获取。 对吧?但是当我尝试这样做时,我得到了一个取消异常,这让我现在发疯了。
任何帮助都将受到高度赞赏!谢谢
edit:obj 是在类外部声明的 ArrayList,用于存储返回值。
这是我的代码由 开始 jbutton 执行
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {
final ArrayList packet_list = new ArrayList();
obj.clear();
try {
worker = new SwingWorker<ArrayList,Integer>(){//initialze swingworker class
@Override
protected void done(){
try {
obj = get();
}
catch (InterruptedException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
}
}
//opens up stuff required to capture the packets
NetworkInterface [] devices = JpcapCaptor.getDeviceList();
int index = (jComboBox5.getSelectedIndex()-1);
JpcapCaptor captor =JpcapCaptor.openDevice(devices[4], 65535, false, 20);
@Override
protected ArrayList doInBackground(){
while(!isCancelled()){
try {
Packet packets = captor.getPacket(); //captures packets
if (packets != null) //filters out null packets
{
//System.out.println(packets);
packet_list.add(packets); //adds each packet to ArrayList
}
Thread.sleep(100);
} catch (InterruptedException ex) {
return packet_list;
}
}
return packet_list;
}
};
worker.execute();
} catch (IOException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
}
}
The stop button simply executes
worker.cancel(); no errors there. and this is the swingworker declaration
private SwingWorker<ArrayList,Integer> worker;
cancel
不只是设置isCancelled
标志供您在闲暇时阅读。那几乎是没用的。如果任务尚未启动,它会阻止任务启动,如果线程已在运行,则可能会主动中断线程。因此,获取CancellationException
是取消正在运行的任务的自然结果。
为了进一步说明这一点,Javadoc on isCancelled
指出:
如果此任务在正常完成之前被取消,则返回 true。
因此,如果返回 true
,则您的任务无法正常完成。您不能取消任务并期望它正常继续。
SwingWorker
文档说"一个抽象类,用于在后台线程中执行冗长的 GUI 交互任务"。但是,对于 GUI 和应用程序生存期,"冗长"的定义是不同的。对于 GUI 来说,100ms 的任务很长,最好由 SwingWorker
完成。对于SwingWorker
来说,10 分钟的任务太长了,因为它的线程池有限,您可能会耗尽。从您的问题描述来看,您确实拥有这一点 - 一个可能运行时间很长的任务。因此,您应该宁愿制作适当的后台线程,也不愿使用SwingWorker
。
在该线程中,您将有一个 AtomicBoolean
或只是一个可以从 EDT 手动设置的 volatile boolean
标志。然后,线程可以将事件与结果一起发布到 EDT。
法典:
class PacketCaptureWorker implements Runnable {
private volatile boolean cancelled = false;
public void cancel() {
cancelled = true;
}
public void run() {
while (!cancelled) {
//do work
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Use the result of your computation on the EDT
}
});
}
}
new Thread(new PacketCaptureWorker()).start();
我尝试使用易失性布尔值而不是使用 worker.cancel(( 作为 swingworker 线程 while 循环,它工作得很好。(至少在表面上(我也设法创建了一个普通的背景线程,这也像一个魅力:D非常感谢你让我头疼!想知道两者中最好的方法是什么。
接下来,我必须使可变布尔值可用于整个类,因为我必须为线程类创建 2 个单独的实例,一个使用 START,另一个使用 STOP。显然,两个不同的实例不能解决变量的同一实例。这是不好的做法吗?