运行 GUI 时关于线程的问题
Question about threads when running a GUI
我目前正在为 Uni 编写一个程序,该程序使用 RFID 跟踪器来跟踪 RFID 标签通过时的移动。这是在模仿钱包穿过房间时的移动。
我在我的 GUI 中添加了一个按钮,用于初始化跟踪器并在标签通过它们时更新我的数据库。他们保持在线 60 秒,一切正常。但是,一旦按下此按钮,我的 GUI 就会冻结,直到进程停止。这可以防止我的 GUI 更新出现 table。
我意识到我需要使用线程,我发现 SwingWorker 看起来不错但是在尝试理解它一段时间后我似乎无法弄明白。
我想知道是否有比我更有经验的人可以告诉我 SwingWorker 是否能满足我的需要,并给我一些关于如何使用它的建议。
以下是我的一些代码片段:
跟踪 JButton:
JButton trackingButton = new JButton("Start Tracking");
trackingButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
RFIDHandler.openRFID(); //runs the RFID readers
} catch (PhidgetException | SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
我的 openRFID 方法的开始:
public static void openRFID() throws PhidgetException, SQLException {
RFID phid1 = new RFID();
RFID phid2 = new RFID();
WalletDAO dao = new WalletDAO();
// Open and start detecting rfid cards
phid1.open(5000); // wait 5 seconds for device to respond
phid2.open(5000); // wait 5 seconds for device to respond
phid1.setAntennaEnabled(true);
phid2.setAntennaEnabled(true);
// Make the RFID Phidget able to detect loss or gain of an rfid card
phid1.addTagListener(new RFIDTagListener() {
public void onTag(RFIDTagEvent e) {
try {
Wallet w = dao.getWallet(e.getTag());
String location = w.getLocation();
System.out.println(location);
if(Objects.equals(location, "Room A")) {
System.out.println(w.getName() + "'s Wallet read");
w.setLocation("Room B");
try {
dao.updateLocation(w);
WalletLocatorInterface.refreshTable(); // refreshes table on GUI
System.out.println("Currently in room B");
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
// ... else statement + another TagListener
System.out.println("Gathering data for 60 seconds\n\n");
try {
Thread.sleep(60000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
phid1.close();
phid2.close();
System.out.println("\nClosed RFIDs");
}
如果您需要我解决任何问题,请告诉我,谢谢。
你的问题是Thread.sleep
。您可以使用 ScheduledExecutorService
完成您在这里所做的事情。只需将您的代码修改为:
...
System.out.println("Gathering data for 60 seconds\n\n");
try {
Thread.sleep(60000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Executors.newScheduledThreadPool(1).schedule(() -> {
phid1.close();
phid2.close();
System.out.println("\nClosed RFIDs");
}, 60, TimeUnit.SECONDS);
}
(对了,我用了一个lambda表达式,可以解释here。)
关于SwingWorker
,我没有多少经验。但是,使用 SwingWorker
,您可以在后台执行操作,而不会在更新时让您的 GUI 卡顿。 但是,如果你没有像@MadProgrammer 所说的那样真正更新 GUI,那么你就不会真正从使用 SwingWorker
.
中受益
当你尝试设计这样的东西时,你需要以一种更超然的方式重新思考你的方法。
我可能会做的第一件事是定义一个可以传递信息的委托,这样更容易处理,因为你不需要乱用 SwingWorker
s PropertyChangeListener
并且它允许您解耦代码...
public interface RFIDWorkerDelegate {
public void locationsUpdated(List<String> locations);
}
接下来,您需要将可能会阻塞 UI 的操作放入 SwingWorker
中 doInBackfground
。
然后您需要决定用您想要的更改更新 UI 的最佳时间。然后你调用 publish
到 "publish" 结果。这些将被传递给在事件调度线程的上下文中调用的 process
方法,例如...
public class RFIDWorker extends SwingWorker<Void, String> {
private RFIDWorkerDelegate delegate;
public RFIDWorker(RFIDWorkerDelegate delegate) {
this.delegate = delegate;
}
@Override
protected void process(List<String> chunks) {
delegate.locationsUpdated(chunks);
}
@Override
protected Void doInBackground() throws Exception {
RFID phid1 = new RFID();
RFID phid2 = new RFID();
WalletDAO dao = new WalletDAO();
// Open and start detecting rfid cards
phid1.open(5000); // wait 5 seconds for device to respond
phid2.open(5000); // wait 5 seconds for device to respond
phid1.setAntennaEnabled(true);
phid2.setAntennaEnabled(true);
// Make the RFID Phidget able to detect loss or gain of an rfid card
phid1.addTagListener(new RFIDTagListener() {
public void onTag(RFIDTagEvent e) {
Wallet w = dao.getWallet(e.getTag());
String location = w.getLocation();
System.out.println(location);
publish(location);
if (Objects.equals(location, "Room A")) {
System.out.println(w.getName() + "'s Wallet read");
w.setLocation("Room B");
try {
dao.updateLocation(w);
WalletLocatorInterface.refreshTable(); // refreshes table on GUI
System.out.println("Currently in room B");
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
System.out.println("Gathering data for 60 seconds\n\n");
try {
Thread.sleep(60000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
phid1.close();
phid2.close();
System.out.println("\nClosed RFIDs");
return null;
}
}
然后你可以称它做类似...
new RFIDWorker(new RFIDWorkerDelegate() {
@Override
public void locationsUpdated(List<String> locations) {
// Update the UI as required
}
});
我目前正在为 Uni 编写一个程序,该程序使用 RFID 跟踪器来跟踪 RFID 标签通过时的移动。这是在模仿钱包穿过房间时的移动。
我在我的 GUI 中添加了一个按钮,用于初始化跟踪器并在标签通过它们时更新我的数据库。他们保持在线 60 秒,一切正常。但是,一旦按下此按钮,我的 GUI 就会冻结,直到进程停止。这可以防止我的 GUI 更新出现 table。
我意识到我需要使用线程,我发现 SwingWorker 看起来不错但是在尝试理解它一段时间后我似乎无法弄明白。
我想知道是否有比我更有经验的人可以告诉我 SwingWorker 是否能满足我的需要,并给我一些关于如何使用它的建议。
以下是我的一些代码片段:
跟踪 JButton:
JButton trackingButton = new JButton("Start Tracking");
trackingButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
RFIDHandler.openRFID(); //runs the RFID readers
} catch (PhidgetException | SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
我的 openRFID 方法的开始:
public static void openRFID() throws PhidgetException, SQLException {
RFID phid1 = new RFID();
RFID phid2 = new RFID();
WalletDAO dao = new WalletDAO();
// Open and start detecting rfid cards
phid1.open(5000); // wait 5 seconds for device to respond
phid2.open(5000); // wait 5 seconds for device to respond
phid1.setAntennaEnabled(true);
phid2.setAntennaEnabled(true);
// Make the RFID Phidget able to detect loss or gain of an rfid card
phid1.addTagListener(new RFIDTagListener() {
public void onTag(RFIDTagEvent e) {
try {
Wallet w = dao.getWallet(e.getTag());
String location = w.getLocation();
System.out.println(location);
if(Objects.equals(location, "Room A")) {
System.out.println(w.getName() + "'s Wallet read");
w.setLocation("Room B");
try {
dao.updateLocation(w);
WalletLocatorInterface.refreshTable(); // refreshes table on GUI
System.out.println("Currently in room B");
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
// ... else statement + another TagListener
System.out.println("Gathering data for 60 seconds\n\n");
try {
Thread.sleep(60000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
phid1.close();
phid2.close();
System.out.println("\nClosed RFIDs");
}
如果您需要我解决任何问题,请告诉我,谢谢。
你的问题是Thread.sleep
。您可以使用 ScheduledExecutorService
完成您在这里所做的事情。只需将您的代码修改为:
...
System.out.println("Gathering data for 60 seconds\n\n");
try {
Thread.sleep(60000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Executors.newScheduledThreadPool(1).schedule(() -> {
phid1.close();
phid2.close();
System.out.println("\nClosed RFIDs");
}, 60, TimeUnit.SECONDS);
}
(对了,我用了一个lambda表达式,可以解释here。)
关于SwingWorker
,我没有多少经验。但是,使用 SwingWorker
,您可以在后台执行操作,而不会在更新时让您的 GUI 卡顿。 但是,如果你没有像@MadProgrammer 所说的那样真正更新 GUI,那么你就不会真正从使用 SwingWorker
.
当你尝试设计这样的东西时,你需要以一种更超然的方式重新思考你的方法。
我可能会做的第一件事是定义一个可以传递信息的委托,这样更容易处理,因为你不需要乱用 SwingWorker
s PropertyChangeListener
并且它允许您解耦代码...
public interface RFIDWorkerDelegate {
public void locationsUpdated(List<String> locations);
}
接下来,您需要将可能会阻塞 UI 的操作放入 SwingWorker
中 doInBackfground
。
然后您需要决定用您想要的更改更新 UI 的最佳时间。然后你调用 publish
到 "publish" 结果。这些将被传递给在事件调度线程的上下文中调用的 process
方法,例如...
public class RFIDWorker extends SwingWorker<Void, String> {
private RFIDWorkerDelegate delegate;
public RFIDWorker(RFIDWorkerDelegate delegate) {
this.delegate = delegate;
}
@Override
protected void process(List<String> chunks) {
delegate.locationsUpdated(chunks);
}
@Override
protected Void doInBackground() throws Exception {
RFID phid1 = new RFID();
RFID phid2 = new RFID();
WalletDAO dao = new WalletDAO();
// Open and start detecting rfid cards
phid1.open(5000); // wait 5 seconds for device to respond
phid2.open(5000); // wait 5 seconds for device to respond
phid1.setAntennaEnabled(true);
phid2.setAntennaEnabled(true);
// Make the RFID Phidget able to detect loss or gain of an rfid card
phid1.addTagListener(new RFIDTagListener() {
public void onTag(RFIDTagEvent e) {
Wallet w = dao.getWallet(e.getTag());
String location = w.getLocation();
System.out.println(location);
publish(location);
if (Objects.equals(location, "Room A")) {
System.out.println(w.getName() + "'s Wallet read");
w.setLocation("Room B");
try {
dao.updateLocation(w);
WalletLocatorInterface.refreshTable(); // refreshes table on GUI
System.out.println("Currently in room B");
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
System.out.println("Gathering data for 60 seconds\n\n");
try {
Thread.sleep(60000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
phid1.close();
phid2.close();
System.out.println("\nClosed RFIDs");
return null;
}
}
然后你可以称它做类似...
new RFIDWorker(new RFIDWorkerDelegate() {
@Override
public void locationsUpdated(List<String> locations) {
// Update the UI as required
}
});