thread example java multithreading swing jcomponent

example - ¿Cómo funciona JComponent.paintImmediately() en Java Swing?



java worker (1)

A mi entender, cuando llamas a paintImmediately, llamas al siguiente código:

Component c = this; Component parent; if(!isShowing()) { return; } JComponent paintingOigin = SwingUtilities.getPaintingOrigin(this); if (paintingOigin != null) { Rectangle rectangle = SwingUtilities.convertRectangle( c, new Rectangle(x, y, w, h), paintingOigin); paintingOigin.paintImmediately(rectangle.x, rectangle.y, rectangle.width, rectangle.height); return; } while(!c.isOpaque()) { parent = c.getParent(); if(parent != null) { x += c.getX(); y += c.getY(); c = parent; } else { break; } if(!(c instanceof JComponent)) { break; } } if(c instanceof JComponent) { ((JComponent)c)._paintImmediately(x,y,w,h); } else { c.repaint(x,y,w,h); }

Entonces, a menos que esto no sea un JComponent , terminas llamando a _paintImmediately() que termina llamando a paint(Graphics) como sugiere el siguiente rastro de pila (capturado de un fragmento de código que publicaré al final de esta publicación):

Thread [pool-1-thread-1] (Suspended) TestPaint$1.paint(Graphics) line: 23 TestPaint$1(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5221 RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1482 RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1413 RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1206 TestPaint$1(JComponent)._paintImmediately(int, int, int, int) line: 5169 TestPaint$1(JComponent).paintImmediately(int, int, int, int) line: 4980 TestPaint$1(JComponent).paintImmediately(Rectangle) line: 4992 TestPaint$3.run() line: 50 ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1110 ThreadPoolExecutor$Worker.run() line: 603 Thread.run() line: 722

Pero si también intentas llamar a repaint() simultáneamente (desde otro subproceso), ves que ambos se ejecutan al mismo tiempo (intenté entrar en el código con debuger y la pintura nunca dejó de aparecer en el otro subproceso) parece que en el Nivel de código Java, no hay mucha sincronización (al menos no pude detectar nada). Entonces, si terminas modificando el estado del componente en el EDT, creo que los resultados son bastante impredecibles y debes evitar esa situación por todos los medios.

Para ilustrar mi punto, intenté modificar el estado de una variable dentro del método de paint , añadí un sleep para aumentar el riesgo de colisiones entre los 2 hilos (EDT y el otro) y parece que no hay sincronización entre los dos Threads (el System.err.println() null de vez en cuando).

Ahora me pregunto por qué necesitas pintar de inmediato. A menos que esté bloqueando el EDT, no hay tantas razones válidas para realizar tal cosa.

A continuación se muestra el código que usé para probar esas cosas (bastante cerca del que se publicó en la pregunta). El código solo intenta comprender lo que está sucediendo, no mostrar cómo realizar la pintura adecuada ni ninguna buena práctica de Swing.

import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import java.util.concurrent.Executors; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.Timer; public class TestPaint { protected void initUI() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setTitle(TestPaint.class.getSimpleName()); final Random rand = new Random(); final JPanel comp = new JPanel() { private String value; @Override public void paint(Graphics g) { value = "hello"; super.paint(g); try { Thread.sleep(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } g.setColor(new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256))); g.fillRect(0, 0, getWidth(), getHeight()); if (SwingUtilities.isEventDispatchThread()) { System.err.println("Painting in the EDT " + getValue()); } else { System.err.println("Not painting in EDT " + getValue()); } value = null; } public String getValue() { return value; } }; frame.add(comp); frame.setSize(400, 400); frame.setLocationRelativeTo(null); frame.setVisible(true); Timer t = new Timer(1, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { comp.repaint(); } }); t.start(); Executors.newSingleThreadExecutor().execute(new Runnable() { @Override public void run() { while (true) { comp.paintImmediately(comp.getBounds()); } } }); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new TestPaint().initUI(); } }); } }

Según tengo entendido, a diferencia de la mayoría de los componentes / operaciones en Swing, llamar a JComponent.repaint () es seguro para subprocesos, es decir, aunque se realiza una solicitud de repintado desde otro subproceso (es decir, no desde EDT), la pintura real solo ocurre en EDT. Debajo del fragmento de código se demuestra esto.

public class PaintingDemo { public static void main(String[] args) { final JFrame frame = new JFrame(); final JPanel p = new MyPanel(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { frame.add(p, BorderLayout.CENTER); frame.setSize(200, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); new Thread("MyThread") { public void run() { while (true) { // Below statements are important to show the difference p.repaint(); p.paintImmediately(p.getBounds()); try { Thread.sleep(1000); } catch(Exception e) {} } } }.start(); } } class MyPanel extends JPanel { @Override public void paint(Graphics g) { System.out.println("paint() called in "+ Thread.currentThread().getName()); super.paint(g); } }

A partir de la salida, se sabe que la pintura se realiza en EDT cuando se invoca repintado () independientemente de a qué hilo se llama, por lo que no hay problemas. Pero, en el caso de la pintura Inmediatamente (), la pintura ocurre en el mismo hilo del que se le llama.

Considere un caso donde EDT está cambiando el estado de un componente y otro subproceso (del cual se invoca inmediatamente paint () () está pintando el mismo componente.

Mi pregunta: En caso de paintImmediately (), ¿cómo se maneja la sincronización entre Event Dispatcher Thread (EDT) y otros hilos?