java / swing: JSlider.setValue блокируется

#java #swing #jslider

#java #swing #jslider

Вопрос:

мой вызов JSlider.setValue иногда блокирует поток, что приводит к взаимоблокировке всего приложения.

вот трассировка стека блокирующего потока.

 Thread [RenderThread] (Suspended)   
JSlider(Component).getMousePosition() line: not available [local variables unavailable] 
SynthSliderUI.calculateThumbLocation() line: not available [local variables unavailable]    
BasicSliderUI$Handler.stateChanged(ChangeEvent) line: not available 
DefaultBoundedRangeModel.fireStateChanged() line: not available [local variables unavailable]   
DefaultBoundedRangeModel.setRangeProperties(int, int, int, int, boolean) line: not available    
DefaultBoundedRangeModel.setValue(int) line: not available  
JSlider.setValue(int) line: not available   
TimeLine.setTime(double) line: 422  
GLFrame.display() line: 302 
GLFrame$2.renderCallback() line: 188    
LWJGLBinding$1.paintGL() line: 49   
LWJGLBinding$1(AWTGLCanvas).paint(Graphics) line: 314   
LWJGLBinding$1(AWTGLCanvas).update(Graphics) line: 343  
GLFrame$2(LWJGLBinding).startRendering() line: 78   
GLFrame$3.run() line: 267   
  

похоже, это связано с внешним видом nimbus, потому что i не выполняется во внешнем виде по умолчанию.

     try 
    {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
    } 
    catch (Exception e) 
    {
        e.printStackTrace();
    }
  

Редактировать:
с помощью invokeLater я получаю это исключение:

 at javax.swing.plaf.synth.SynthTreeUI.paint(Unknown Source)
at javax.swing.plaf.synth.SynthTreeUI.update(Unknown Source)
at javax.swing.JComponent.paintComponent(Unknown Source)
at javax.swing.JComponent.paint(Unknown Source)
at javax.swing.JComponent.paintToOffscreen(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
at javax.swing.RepaintManager.paint(Unknown Source)
at javax.swing.JComponent._paintImmediately(Unknown Source)
at javax.swing.JComponent.paintImmediately(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
  

Комментарии:

1. убедитесь, что setValue вызывается в EDT

2. хорошо, спасибо. какой рекомендуемый способ сделать это?

3. SwingUtilites.invokeLater(new Runnable(){ public void run(){ / *Your code */ }})

4. @Frozen, вы должны перенести это в ответ, чтобы @clamp мог его принять.

5. @MeBigFatGuy Я думал, что @kleopatra сделает это, поэтому я решил подождать 🙂

Ответ №1:

Компоненты Swing не являются потокобезопасными. Все, что изменяет компонент Swing после того, как он уже показан, должно выполняться в так называемом потоке отправки событий (EDT). Для достижения этого Swing предоставляет вам функции:

Два из них служат для выполнения кода:

 SwingUtilities.invokeLater(Runnable)
SwingUtilities.invokeAndWait(Runnable)
  

Их назначение вытекает из их названия. Третий — это

 SwingUtilities.isEventDispatchThread()
  

Если это возвращает true , значит, вы уже находитесь в EDT и можете выполнять код напрямую.

Обратите внимание, что это invokeAndWait вызовет исключение, если оно вызывается непосредственно из EDT ( invokeLater не будет, но это все еще не рекомендуется делать), поэтому код, который может быть вызван как из EDT, так и из побочного потока, следует записать следующим образом:

 if (SwingUtilities.isEventDispatchThread()) {
    // code
} else {
    SwingUtilities.invokeLater(new Runnable () {
        public void run () {
            // code
        }
    });
}
  

В целях простоты и удобочитаемости вы можете извлечь внутренний код из метода.

Комментарии:

1. 1 хороший ответ. Просто обратите внимание, что при использовании SwingUtilities.invokeLater(Runnable) вам не нужно проверять, что он не запускается в EDT, что вы должны сделать при использовании SwingUtilities.invokeAndWait(Runnable) . Исключение будет выдано только invokeAndWait. Это полезно проверить, поскольку вы не приведете к тому, что ваш код попадет в конец очереди.

2. Спасибо. на самом деле я получаю то исключение, о котором вы говорите, при запуске, даже если я использую invokeLater. смотрите мою правку в моем исходном сообщении

3. @Boro Спасибо, исправил это. @clamp Не могли бы вы, пожалуйста, опубликовать само исключение, а не только трассировку стека?

4. ну, само исключение — это просто исключение nullpointer, начинающееся со стека, который я опубликовал.

5. Похоже, что у вас есть другие методы, изменяющие Swing, которые вызываются не из EDT (см. bugs.sun.com/bugdatabase/view_bug.do?bug_id=4524304 ). Пожалуйста, проверьте, верно ли это.