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

时间:2011-12-17 13:22:24

标签: java swing cursor event-dispatch-thread

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

感谢

4 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

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

答案 2 :(得分:2)

答案 3 :(得分:0)

This 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);
    }
}