java中如何检测主线程何时冻结GUI?

发布于 2024-12-21 19:51:25 字数 65 浏览 2 评论 0原文

我想检测主线程中的一些耗时操作何时导致 gui 冻结。 我的目标是自动设置和取消设置等待光标。

谢谢

I want to detect when some time consumption operations in main thread cause gui freeze.
My target is to set and unset wait cursor automatically.

thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

讽刺将军 2024-12-28 19:51:25

我认为你是本末倒置:你的主线程首先不应该执行任何耗时的操作 - 它们应该始终在单独的线程中具体化,以便你的 GUI 可以保持响应(例如显示状态)操作,或提供中止操作的可能性)。

I think you're putting the cart before the horse: Your main thread shouldn't do any time-consuming operations in the first place - they should always be externalized in separate threads, so that your GUI can stay responsive (and e.g. show status on the operations, or provide the possibility to abort them).

对不⑦ 2024-12-28 19:51:25

您可以让一个线程轮询 GUI 线程的堆栈跟踪以确定它是空闲还是忙碌。如果过于频繁,您可以将其正在执行的操作(堆栈跟踪)记录到日志中。最初,记录每个非空闲堆栈跟踪并找出哪些不值得记录可能会很有趣。

You can have a thread which polls the GUI thread's stack trace to determine whether it is idle or busy. If busy too often, you can log what it is doing (the stack trace) to a log. Initially it might be interesting to record every non-idle stack trace and work out which ones are not worth logging.

如若梦似彩虹 2024-12-28 19:51:25

EDT 锁定检测代码将通过添加看门狗来完成这项工作。

EventQueueWithWD.java

import java.awt.*;
import java.awt.event.*;
import java.util.*;

/**
 * Alternative events dispatching queue. The benefit over the
 * default Event Dispatch queue is that you can add as many
 * watchdog timers as you need and they will trigger arbitrary
 * actions when processing of single event will take longer than
 * one timer period.
 * <p/>
 * Timers can be of two types:
 * <ul>
 * <li><b>Repetitive</b> - action can be triggered multiple times
 * for the same "lengthy" event dispatching.
 * </li>
 * <li><b>Non-repetitive</b> - action can be triggered only once
 * per event dispatching.</li>
 * </ul>
 * <p/>
 * The queue records time of the event dispatching start.  This
 * time is used by the timers to check if dispatching takes
 * longer than their periods. If so the timers trigger associated
 * actions.
 * <p/>
 * In order to use this queue application should call
 * <code>install()</code> method. This method will create,
 * initialize and register the alternative queue as appropriate.
 * It also will return the instance of the queue for further
 * interactions. Here's an example of how it can be done:
 * <p/>
 * <pre>
 * <p/>
 *  EventQueueWithWD queue = EventQueueWithWD.install();
 *  Action edtOverloadReport = ...;
 * <p/>
 *  // install single-shot wg to report EDT overload after
 *  // 10-seconds timeout
 *  queue.addWatchdog(10000, edtOverloadReport, false);
 * <p/>
 * </pre>
 */
public class EventQueueWithWD extends EventQueue {
  // Main timer
  private final java.util.Timer timer = new java.util.Timer(true);

  // Group of informational fields for describing the event
  private final Object eventChangeLock = new Object();
  private volatile long eventDispatchingStart = -1;
  private volatile AWTEvent event = null;

  /**
   * Hidden utility constructor.
   */
  private EventQueueWithWD() { }

  /**
   * Install alternative queue.
   *
   * @return instance of queue installed.
   */
  public static EventQueueWithWD install() {
    EventQueue eventQueue =
        Toolkit.getDefaultToolkit().getSystemEventQueue();
    EventQueueWithWD newEventQueue = new EventQueueWithWD();
    eventQueue.push(newEventQueue);
    return newEventQueue;
  }

  /**
   * Record the event and continue with usual dispatching.
   *
   * @param anEvent event to dispatch.
   */
  protected void dispatchEvent(AWTEvent anEvent) {
    setEventDispatchingStart(anEvent, System.currentTimeMillis());
    super.dispatchEvent(anEvent);
    setEventDispatchingStart(null, -1);
  }

  /**
   * Register event and dispatching start time.
   *
   * @param anEvent   event.
   * @param timestamp dispatching start time.
   */
  private void setEventDispatchingStart(AWTEvent anEvent,
                                        long timestamp) {
    synchronized (eventChangeLock) {
      event = anEvent;
      eventDispatchingStart = timestamp;
    }
  }

  /**
   * Add watchdog timer. Timer will trigger <code>listener</code>
   * if the queue dispatching event longer than specified
   * <code>maxProcessingTime</code>. If the timer is
   * <code>repetitive</code> then it will trigger additional
   * events if the processing 2x, 3x and further longer than
   * <code>maxProcessingTime</code>.
   *
   * @param maxProcessingTime maximum processing time.
   * @param listener          listener for events. The listener
   *                          will receive <code>AWTEvent</code>
   *                          as source of event.
   * @param repetitive        TRUE to trigger consequent events
   *                          for 2x, 3x and further periods.
   */
  public void addWatchdog(long maxProcessingTime,
                          ActionListener listener,
                          boolean repetitive) {
    Watchdog checker = new Watchdog(maxProcessingTime, listener,
        repetitive);
    timer.schedule(checker, maxProcessingTime,
        maxProcessingTime);
  }

  /**
   * Checks if the processing of the event is longer than the
   * specified <code>maxProcessingTime</code>. If so then
   * listener is notified.
   */
  private class Watchdog extends TimerTask {
    // Settings
    private final long maxProcessingTime;
    private final ActionListener listener;
    private final boolean repetitive;

    // Event reported as "lengthy" for the last time. Used to
    // prevent repetitive behaviour in non-repeatitive timers.
    private AWTEvent lastReportedEvent = null;

    /**
     * Creates timer.
     *
     * @param maxProcessingTime maximum event processing time
     *                           before listener is notified.
     * @param listener          listener to notify.
     * @param repetitive       TRUE to allow consequent
     *                           notifications for the same event
     */
    private Watchdog(long maxProcessingTime,
                    ActionListener listener,
                    boolean repetitive) {
      if (listener == null)
        throw new IllegalArgumentException(
            "Listener cannot be null.");
      if (maxProcessingTime < 0)
        throw new IllegalArgumentException(
          "Max locking period should be greater than zero");
      this.maxProcessingTime = maxProcessingTime;
      this.listener = listener;
      this.repetitive = repetitive;
    }

    public void run() {
      long time;
      AWTEvent currentEvent;

      // Get current event requisites
      synchronized (eventChangeLock) {
        time = eventDispatchingStart;
        currentEvent = event;
      }

      long currentTime = System.currentTimeMillis();

      // Check if event is being processed longer than allowed
      if (time != -1 && (currentTime - time > maxProcessingTime) &&
          (repetitive || currentEvent != lastReportedEvent)) {
        listener.actionPerformed(
            new ActionEvent(currentEvent, -1, null));
        lastReportedEvent = currentEvent;
      }
    }
  }
}

SampleEQUsage.java

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.Date;

/**
 * Sample usage of <code>EventQueueWithWD</code> class.
 */
public class SampleEQUsage extends JFrame
{
    public SampleEQUsage()
    {
        super("Sample EQ Usage");
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        getContentPane().add(new JButton(new AbstractAction("Go")
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println();
                System.out.println(new Date());
                try
                {
                    // Sleep for 10 seconds
                    Thread.sleep(10000);
                } catch (InterruptedException e1)
                {
                }
            }
        }));

        setSize(100, 100);
    }

    public static void main(String[] args)
    {
        initQueue();

        SampleEQUsage sequ = new SampleEQUsage();
        sequ.setVisible(true);
    }

    // Install and init the alternative queue
    private static void initQueue()
    {
        EventQueueWithWD queue = EventQueueWithWD.install();

        // Install 3-seconds single-shot watchdog timer
        queue.addWatchdog(3000, new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println(new Date() + " 3 seconds - single-shot");
            }
        }, false);

        // Install 3-seconds multi-shot watchdog timer
        queue.addWatchdog(3000, new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println(new Date() + " 3 seconds - multi-shot");
            }
        }, true);

        // Install 11-seconds multi-shot watchdog timer
        queue.addWatchdog(11000, new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println(new Date() + " 11 seconds - multi-shot");
            }
        }, true);
    }
}

This EDT lockup detection code will do the job by adding watch dogs.

EventQueueWithWD.java:

import java.awt.*;
import java.awt.event.*;
import java.util.*;

/**
 * Alternative events dispatching queue. The benefit over the
 * default Event Dispatch queue is that you can add as many
 * watchdog timers as you need and they will trigger arbitrary
 * actions when processing of single event will take longer than
 * one timer period.
 * <p/>
 * Timers can be of two types:
 * <ul>
 * <li><b>Repetitive</b> - action can be triggered multiple times
 * for the same "lengthy" event dispatching.
 * </li>
 * <li><b>Non-repetitive</b> - action can be triggered only once
 * per event dispatching.</li>
 * </ul>
 * <p/>
 * The queue records time of the event dispatching start.  This
 * time is used by the timers to check if dispatching takes
 * longer than their periods. If so the timers trigger associated
 * actions.
 * <p/>
 * In order to use this queue application should call
 * <code>install()</code> method. This method will create,
 * initialize and register the alternative queue as appropriate.
 * It also will return the instance of the queue for further
 * interactions. Here's an example of how it can be done:
 * <p/>
 * <pre>
 * <p/>
 *  EventQueueWithWD queue = EventQueueWithWD.install();
 *  Action edtOverloadReport = ...;
 * <p/>
 *  // install single-shot wg to report EDT overload after
 *  // 10-seconds timeout
 *  queue.addWatchdog(10000, edtOverloadReport, false);
 * <p/>
 * </pre>
 */
public class EventQueueWithWD extends EventQueue {
  // Main timer
  private final java.util.Timer timer = new java.util.Timer(true);

  // Group of informational fields for describing the event
  private final Object eventChangeLock = new Object();
  private volatile long eventDispatchingStart = -1;
  private volatile AWTEvent event = null;

  /**
   * Hidden utility constructor.
   */
  private EventQueueWithWD() { }

  /**
   * Install alternative queue.
   *
   * @return instance of queue installed.
   */
  public static EventQueueWithWD install() {
    EventQueue eventQueue =
        Toolkit.getDefaultToolkit().getSystemEventQueue();
    EventQueueWithWD newEventQueue = new EventQueueWithWD();
    eventQueue.push(newEventQueue);
    return newEventQueue;
  }

  /**
   * Record the event and continue with usual dispatching.
   *
   * @param anEvent event to dispatch.
   */
  protected void dispatchEvent(AWTEvent anEvent) {
    setEventDispatchingStart(anEvent, System.currentTimeMillis());
    super.dispatchEvent(anEvent);
    setEventDispatchingStart(null, -1);
  }

  /**
   * Register event and dispatching start time.
   *
   * @param anEvent   event.
   * @param timestamp dispatching start time.
   */
  private void setEventDispatchingStart(AWTEvent anEvent,
                                        long timestamp) {
    synchronized (eventChangeLock) {
      event = anEvent;
      eventDispatchingStart = timestamp;
    }
  }

  /**
   * Add watchdog timer. Timer will trigger <code>listener</code>
   * if the queue dispatching event longer than specified
   * <code>maxProcessingTime</code>. If the timer is
   * <code>repetitive</code> then it will trigger additional
   * events if the processing 2x, 3x and further longer than
   * <code>maxProcessingTime</code>.
   *
   * @param maxProcessingTime maximum processing time.
   * @param listener          listener for events. The listener
   *                          will receive <code>AWTEvent</code>
   *                          as source of event.
   * @param repetitive        TRUE to trigger consequent events
   *                          for 2x, 3x and further periods.
   */
  public void addWatchdog(long maxProcessingTime,
                          ActionListener listener,
                          boolean repetitive) {
    Watchdog checker = new Watchdog(maxProcessingTime, listener,
        repetitive);
    timer.schedule(checker, maxProcessingTime,
        maxProcessingTime);
  }

  /**
   * Checks if the processing of the event is longer than the
   * specified <code>maxProcessingTime</code>. If so then
   * listener is notified.
   */
  private class Watchdog extends TimerTask {
    // Settings
    private final long maxProcessingTime;
    private final ActionListener listener;
    private final boolean repetitive;

    // Event reported as "lengthy" for the last time. Used to
    // prevent repetitive behaviour in non-repeatitive timers.
    private AWTEvent lastReportedEvent = null;

    /**
     * Creates timer.
     *
     * @param maxProcessingTime maximum event processing time
     *                           before listener is notified.
     * @param listener          listener to notify.
     * @param repetitive       TRUE to allow consequent
     *                           notifications for the same event
     */
    private Watchdog(long maxProcessingTime,
                    ActionListener listener,
                    boolean repetitive) {
      if (listener == null)
        throw new IllegalArgumentException(
            "Listener cannot be null.");
      if (maxProcessingTime < 0)
        throw new IllegalArgumentException(
          "Max locking period should be greater than zero");
      this.maxProcessingTime = maxProcessingTime;
      this.listener = listener;
      this.repetitive = repetitive;
    }

    public void run() {
      long time;
      AWTEvent currentEvent;

      // Get current event requisites
      synchronized (eventChangeLock) {
        time = eventDispatchingStart;
        currentEvent = event;
      }

      long currentTime = System.currentTimeMillis();

      // Check if event is being processed longer than allowed
      if (time != -1 && (currentTime - time > maxProcessingTime) &&
          (repetitive || currentEvent != lastReportedEvent)) {
        listener.actionPerformed(
            new ActionEvent(currentEvent, -1, null));
        lastReportedEvent = currentEvent;
      }
    }
  }
}

SampleEQUsage.java:

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.Date;

/**
 * Sample usage of <code>EventQueueWithWD</code> class.
 */
public class SampleEQUsage extends JFrame
{
    public SampleEQUsage()
    {
        super("Sample EQ Usage");
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        getContentPane().add(new JButton(new AbstractAction("Go")
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println();
                System.out.println(new Date());
                try
                {
                    // Sleep for 10 seconds
                    Thread.sleep(10000);
                } catch (InterruptedException e1)
                {
                }
            }
        }));

        setSize(100, 100);
    }

    public static void main(String[] args)
    {
        initQueue();

        SampleEQUsage sequ = new SampleEQUsage();
        sequ.setVisible(true);
    }

    // Install and init the alternative queue
    private static void initQueue()
    {
        EventQueueWithWD queue = EventQueueWithWD.install();

        // Install 3-seconds single-shot watchdog timer
        queue.addWatchdog(3000, new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println(new Date() + " 3 seconds - single-shot");
            }
        }, false);

        // Install 3-seconds multi-shot watchdog timer
        queue.addWatchdog(3000, new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println(new Date() + " 3 seconds - multi-shot");
            }
        }, true);

        // Install 11-seconds multi-shot watchdog timer
        queue.addWatchdog(11000, new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println(new Date() + " 11 seconds - multi-shot");
            }
        }, true);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文