【问题标题】:How to display the time in a jpanel while a background thread is running?如何在后台线程运行时在 jpanel 中显示时间?
【发布时间】:2015-12-02 21:14:35
【问题描述】:

我试图在 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);     
    }
}

【问题讨论】:

  • 显然时间线程应该获得更高的优先级...... 关于线程优先级的决定并不总是显而易见的。您需要多精确的时间显示?请记住,您所做的只是将其显示在屏幕上。
  • 另外,请注意过早的优化。你问这个问题是不是因为你已经试过了,结果不满意?或者您是否试图预测可能存在或不存在的问题?
  • 不要假设TimerThread 的准确性,这些都不能保证他们等待的时间是准确的,只是它会“至少”给定的时间。使用锚值并计算过去的时间量
  • 该程序按我希望的方式工作,我只是好奇这是否是一个好方法。而且我不明白为什么后台线程不会影响 Timer 以及如何不停地关闭后台线程。 @MadProgrammer 所以我应该使用 System.currentTimeMillis() ?
  • 疯狂物理学家几乎回答了所有问题。但是如何避免 stop() ?

标签: java multithreading swing


【解决方案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() 方法。

就停止线程而言,我建议使用一种简单的基于中断的机制来避免使用已弃用的stop() 方法。不要使用stop(),而是在昂贵的计算线程上使用interrupt()。这通常会(但请阅读文档)做以下两件事之一:

  1. 在计算线程中引发中断的异常。
  2. 设置线程的中断状态。

然后,您的计算工作就是响应这两种情况。通常,您会将昂贵的部分包含在 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() 为您提供了更多的控制权,尤其是当您的线程需要特殊清理时。

【讨论】:

    猜你喜欢
    • 2020-04-25
    • 1970-01-01
    • 2019-07-22
    • 2018-02-05
    • 2010-10-13
    • 1970-01-01
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多