To my understanding, 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,y,w,h);
} else {
c.repaint(x,y,w,h);
}
So, unless this is not a JComponent
, you end up calling _paintImmediately()
which ends up calling paint(Graphics)
as suggests the stack trace below (captured from a piece of code I will post at the end of this post):
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
But if you try to also call repaint()
simultaneously (from another Thread), you see that both run at the same time (I tried stepping in the code with debuger and painting never stopped occuring in the other Thread) it seems that at the Java code level, there is not much synchronization (at least I could not spot anything). So if you end up modifying the component state in the EDT, I believe that results are quite unpredictable and you should avoid such situation by all means.
Just to illustrate my point, I tried modifying the state of a variable within the paint
method, add a sleep
to increase the risk of collisions between the 2 Threads (EDT and the other) and it obvisouly seems that there is no synchronization between the two Threads (the System.err.println()
outputted null
from time to time).
Now I would wonder why you need to perform a paintImmediately. Unless you are blocking the EDT, there is not so many valid reasons to perform such thing.
Below is the code I used to test those things (pretty close the one posted in the question). The code is only meant to try to understand what is going on, not to show how to perform proper painting nor any good Swing practice.
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();
}
});
}
}
JComponent
docs referencepaintImmediately()
andrepaint(..)
where as source is different. I must ask though in your snippet we would never be overridingpaint(..)
of aJComponent
so why didnt you usepaintComponent
(it still seems to reproduce results)? And another question why will increasing thesleep(int milis)
not increase collisions? And also why do you callpaintImmediately
from a thread other than EDT, (because I see once called on EDT no null will occur)? – Inexistent