JComponent. How does paintimmediately () run in Java swing?
My understanding:
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); } }
As can be seen from the output, when repaint () is called, the drawing is completed in EDT, regardless of which thread is called - so there is no problem However, in the case of paintimmediately () - the painting takes place in the same thread that calls it
Consider the case that EDT is changing the state of the component, and the other thread (calling paintImmediately () from it) is drawing the same components.
My question: how about the synchronization between the event dispatcher thread (EDT) and other threads in the case of paintimmediately()?
Solution
As far as I know, when you call paintimmediately, you call the following code:
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,h); } else { c.repaint(x,h); }
So unless this is not a JComponent, you will eventually call_ PaintImmediately (), finally calling paint (Graphics), I recommend the following stack trace (from a code I'll post at the end of this article):
Thread [pool-1-thread-1] (Suspended) TestPaint$1.paint(Graphics) line: 23 TestPaint$1(JComponent).paintToOffscreen(Graphics,int,int) line: 5221 RepaintManager$PaintManager.paintDoubleBuffered(JComponent,Image,Graphics,int) line: 1482 RepaintManager$PaintManager.paint(JComponent,JComponent,int) line: 1413 RepaintManager.paint(JComponent,int) line: 1206 TestPaint$1(JComponent)._paintImmediately(int,int) line: 5169 TestPaint$1(JComponent).paintImmediately(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
But if you try to call repaint () at the same time (from another thread), you will see that they run at the same time (I try to execute code with the debugger and painting never stops in another thread). It seems that there is not much synchronization at the Java code level (at least I can't find anything) So if you finally modify the component state in EDT, I believe the result is unpredictable. You should avoid this situation
To illustrate my point, I try to modify the state of a variable in the paint method and add a sleep to increase the risk of conflict between two threads (EDT and another thread), and it seems that there is no synchronous thread between them (system. Err. Println() outputs null from time to time)
Now I want to know why you need to execute a paint immediately Unless you stop EDT, there are not many valid reasons to do such a thing
Here's the code I used to test these things (very close to one of the problems) The code is just to try to understand what happened, not to show how to perform the correct painting or any good swing exercise
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,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(); } }); } }