简体   繁体   English

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

[英]How to detect when main thread freeze GUI in java?

I want to detect when some time consumption operations in main thread cause gui freeze. 我想检测主线程中的某些时间消耗操作导致gui冻结。 My target is to set and unset wait cursor automatically. 我的目标是自动设置和取消设置等待光标。

thanks 谢谢

我认为你把车放在马前:你的主线程首先不应该进行任何耗时的操作 - 它们应该总是在不同的线程中外部化,这样你的GUI就能保持响应(例如显示状态)关于操作,或提供中止它们的可能性)。

You can have a thread which polls the GUI thread's stack trace to determine whether it is idle or busy. 您可以拥有一个线程来轮询GUI线程的堆栈跟踪,以确定它是空闲还是忙碌。 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. 最初记录每个非空闲堆栈跟踪并确定哪些不值得记录可能会很有趣。

This EDT lockup detection code will do the job by adding watch dogs. EDT锁定检测代码将通过添加看门狗来完成工作。

EventQueueWithWD.java : 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 : 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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM