我尝试在后台线程执行昂贵操作时在jpanel中显示时间。显然,时间线程应该比后台线程获得更高的优先级,以准确显示时间。
我已经写了以下解决方案,但我确信它可以做得更好。你有什么建议吗?
另外:如何在不使用.stop()的情况下正确关闭后台线程?
编辑:在没有优先级设置的情况下测试程序后,它也有效,但我不知道为什么。
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.WindowConstants;
public class Test1 extends JPanel{
private static final long serialVersionUID = 1L;
private int time = 23*3600+59*60+30;
private Thread operationThread;
public Test1(){
JLabel text = new JLabel();
Timer timeDisplayer = new Timer(1000, e -> {
//Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
text.setText((time/3600)%24+":"+(time/60)%60+":"+(time%60));
time+=1;
});
timeDisplayer.setInitialDelay(0);
JButton b = new JButton("Restart operation");
b.addActionListener(arg0 -> makeNewThread());
add(b);
add(text);
timeDisplayer.start();
}
private void makeNewThread(){
if(operationThread!=null && operationThread.isAlive())
operationThread.stop();
operationThread = new Thread(() -> {
//Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
//expensiveOperation();
});
operationThread.start();
}
public static void main(String[] args){
JFrame f = new JFrame();
Test1 t = new Test1();
f.setLocation(400,50);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setSize(100,100);
f.setContentPane(t);
f.setVisible(true);
}
}
答案 0 :(得分:1)
通常,您显示的内容(例如计时器)不必快速刷新以获得视觉上吸引人的内容。一秒钟的延迟是计算机时间的大量时间,因此没有必要给定时器更新线程额外的优先权。
如果可以显着改善swing Timer课程,则可以使用。计时器的工作方式是向您的处理程序触发ActionEvent
,该处理程序在事件派发线程上排队。这意味着事件触发和侦听器执行之间可能存在延迟。处理这种情况的更好方法是在某处存储System.currentTimeMillis
的值并在回调中使用它:
...
private long startMillis;
public Test1()
{
...
Timer timeDisplayer = new Timer(1000, e -> {
time = (System.currentTimeMillis() - startMillis + 500L) / 1000L;
text.setText(((time / 3600) % 24) + ":" + ((time / 60) % 60) + ":" + (time % 60));
});
timeDisplayer.setInitialDelay(0);
...
startMillis = System.currentTimeMillis()
timeDisplayer.start();
}
这还有一个额外的好处,你可以根据需要加速你的计时器,增加亚秒级精度等。如果你决定这样做,最好尽可能地延迟时间计算例如,通过覆盖显示时间的组件的paintComponent()
方法。
就停止线程而言,我建议使用一种简单的基于interrput的机制来避免使用已弃用的stop()
方法。而不是使用stop()
,在昂贵的计算线程上使用interrupt()
。这通常(但阅读文档)做两件事之一:
然后你的计算工作就是回应这两种情况。通常,您将昂贵的部分包含在try-catch块中,并尝试定期检查中断状态。这是一个例子:
private expensiveOperation()
{
try {
for(SomeObject among : millionsOfItems) {
// Do stuff
if(Thread.currentThread().interrupted()) {
// Possibly log the interruption as you would for the exception
break;
}
}
} catch(InterruptedException ie) {
// Probably just ignore or log it somehow since this is really a normal condition
} finally {
// If your thread needs special cleanup, this is the place to put it
// This block will be triggered by exceptions as well as breaks
}
}
您可以使用interrupted()
或isInterrupted()
检查线程的中断标志。它们之间唯一真正的区别是前者在检查时清除中断状态,而后者则没有。如果您只想进行一次性检查并让线程死亡,那么使用哪一个并不重要。
这种机制的优点是它比调用stop()
给你更多的控制,特别是如果你的线程需要特殊的清理。