所有其他进程完成后,使用 InvokeLater 显示的框架
恐怕这个有点棘手,因为我无法在我为这个问题编写的示例中重现该问题(下面的示例完美运行)。希望有人能够了解实际应用程序可能出现的问题。 我编写了一个执行多个长文本操作的应用程序。每个操作都在其自己的线程中完成。有一个由线程更新的框架,让用户可以看到一切的进展情况。
问题在于,只有在所有线程完成其作业后,框架才会显示发送给它的所有更新。
我已将整个应用程序简化为下面的代码,但正如我所说,问题是它在这里工作。任何想法都非常受欢迎。
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Main {
private MyFrame frame;
private ExecutorService executorService;
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
main.startProcess();
}
public void startProcess() throws InterruptedException {
// Initialize the frame
frame = new MyFrame();
EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
// Initialize executorService for 3 threads and also 6 runnables
executorService = Executors.newFixedThreadPool(3);
MyRunnable runnable;
for(int i = 0; i < 6; i++) {
runnable = new MyRunnable(this, i);
executorService.execute(runnable);
}
// Start runnables
executorService.shutdown();
// Wait until all runnables are executed
while (!executorService.isTerminated()) {
Thread.sleep(10000);
}
// When all runnables are done close the frame
EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setVisible(false);
}
});
}
// Update the frame display
public synchronized void updateDisplay(final String update) {
EventQueue.invokeLater(new Runnable() {
public void run() {
frame.updateDisplay(update);
}
});
}
private class MyFrame extends JFrame {
private JPanel contentPane;
private JLabel lblDisplay;
public MyFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = new JPanel();
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
lblDisplay = new JLabel("Display");
contentPane.add(lblDisplay, BorderLayout.CENTER);
pack();
}
public void updateDisplay(String update) {
lblDisplay.setText(update);
pack();
}
}
private class MyRunnable implements Runnable {
private int id;
private Main main;
public MyRunnable (Main main, int id) {
this.main = main;
this.id = id;
}
@Override
public void run() {
for(int i = 0; i < 3; i++) {
main.updateDisplay("Runnable " + id + " stepped " + i + " times.");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
I'm afraid that this one is a little tricky since I couldn't recreate the issue in the example I wrote for this question (the example below works perfectly). Hopefully someone may have a clue as to possible problems with the actual application.
I wrote a app with performs several long text operations. Each operation is done in its own thread. There is a Frame that is updated by the threads to let a user see how everything is progressing.
The problem is that the frame is displayed with all the updates that were sent to it only after all the threads are done with their jobs.
I've simplified the entire app into the code below but as I said the problem is that it works here. Any ideas are very welcome.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Main {
private MyFrame frame;
private ExecutorService executorService;
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
main.startProcess();
}
public void startProcess() throws InterruptedException {
// Initialize the frame
frame = new MyFrame();
EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
// Initialize executorService for 3 threads and also 6 runnables
executorService = Executors.newFixedThreadPool(3);
MyRunnable runnable;
for(int i = 0; i < 6; i++) {
runnable = new MyRunnable(this, i);
executorService.execute(runnable);
}
// Start runnables
executorService.shutdown();
// Wait until all runnables are executed
while (!executorService.isTerminated()) {
Thread.sleep(10000);
}
// When all runnables are done close the frame
EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setVisible(false);
}
});
}
// Update the frame display
public synchronized void updateDisplay(final String update) {
EventQueue.invokeLater(new Runnable() {
public void run() {
frame.updateDisplay(update);
}
});
}
private class MyFrame extends JFrame {
private JPanel contentPane;
private JLabel lblDisplay;
public MyFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = new JPanel();
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
lblDisplay = new JLabel("Display");
contentPane.add(lblDisplay, BorderLayout.CENTER);
pack();
}
public void updateDisplay(String update) {
lblDisplay.setText(update);
pack();
}
}
private class MyRunnable implements Runnable {
private int id;
private Main main;
public MyRunnable (Main main, int id) {
this.main = main;
this.id = id;
}
@Override
public void run() {
for(int i = 0; i < 3; i++) {
main.updateDisplay("Runnable " + id + " stepped " + i + " times.");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
SwingWorker 的这项工作,只有一个 JFrame,例如 此处
来自
Runnable#Thread
GUI 必须包装到invokeLater()
示例 此处或调用 来自执行器的 SwingWorker
that job for SwingWorker and only one JFrame, example here
output from
Runnable#Thread
to the GUI must be wrapped intoinvokeLater()
example hereor invoke SwingWorker from Executor