Как определить, когда основной поток замораживает графический интерфейс в java?

Я хочу определить, когда некоторые операции потребления времени в основном потоке вызывают зависание графического интерфейса. Моя цель - автоматически устанавливать и отключать курсор ожидания.

спасибо


person blow    schedule 17.12.2011    source источник
comment
Это не имеет особого смысла. Если графический интерфейс завис, вы не сможете установить курсор ожидания: графический интерфейс заморожен.   -  person JB Nizet    schedule 17.12.2011
comment
@blow можно извлечь все события, ожидающие в потоке отправки событий, проверить нарушения, для Reapaint Manager, но почему???, вы должны вычислить (если мы говорим об AWT, Swing и их производных), что все события в EDT будет сделано (вывод на экран) в один момент,   -  person mKorbel    schedule 17.12.2011
comment
@blow: добавлен тег «качели». пожалуйста, поправьте, если я ошибаюсь   -  person home    schedule 17.12.2011
comment
Каюсь в своем грехе: видимо, установка курсора меняет его сразу, даже посреди трудоемкого действия в EDT. Таким образом, вопрос имеет смысл.   -  person JB Nizet    schedule 17.12.2011


Ответы (4)


Я думаю, это может быть полезно: http://www.javaspecialists.eu/archive/Issue075.html и http://www.javaworld.com/javaworld/javatips/jw-javatip87.html.

person Igor Nardin    schedule 19.12.2011
comment
Вы должны суммировать эти ссылки в своем ответе на случай, если они умрут. В противном случае ваш ответ становится бесполезным. - person BullyWiiPlaza; 23.11.2018

Я думаю, что вы ставите телегу впереди лошади: ваш основной поток не должен в первую очередь выполнять какие-либо трудоемкие операции — они всегда должны быть реализованы в отдельных потоках, чтобы ваш графический интерфейс мог оставаться отзывчивым (и, например, показывать статус на операции или предоставить возможность их прерывания).

person codeling    schedule 17.12.2011

У вас может быть поток, который опрашивает трассировку стека потока GUI, чтобы определить, простаивает он или занят. Если он занят слишком часто, вы можете записать в журнал то, что он делает (трассировку стека). Первоначально может быть интересно записать каждую трассировку стека, которая не простаивает, и выяснить, какие из них не стоит регистрировать.

person Peter Lawrey    schedule 17.12.2011

Этот код обнаружения блокировки 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);
    }
}
person BullyWiiPlaza    schedule 04.05.2019