Java concurrent exception
Hi I have a paint method that draws an image and I have another method that constantly changes the image to be drawn, however I am experiencing concurrency exceptions over and over again. What is the most efficient way to solve this problem? I know that I could use synchronized blocks on a buffered image, but then it generates warnings about synchronized final variable.
private BufferedImage img;
public void modImage(BufferedImage image) {
img = image;
}
public void paintComponent(Graphics g) {
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
source to share
You have a race condition because the value img
can be changed in a different thread between the conditional expression if
and use img
indrawImage
if (img != null) { // <-- img can be non-null here
g.drawImage(img, 0, 0, this); // <-- img can now be null
}
You can assign to a img
local variable and then use the local variable instead img
in the above code. The local variable will remain constant even if img
changed to another object on a different thread
final BufferedImage localImg = img; // <-- localImg won't change locally
if (localImg != null) {
g.drawImage(localImg, 0, 0, this);
}
In addition to this, it img
must be declared as volatile
, so its value is not thread-locally cached; changes in one thread will be visible to others.
private volatile BufferedImage img;
Be aware that declaring a variable as volatile
will cause synchronization to occur every time the variable is accessed; so sync is still happening. However, synchronization happens in the link itself img
, not in the object BufferedImage
to which it refers, so there is no problem here if img
any null
.
source to share
There is a thread safety issue because the content BufferedImage
can be changed when the method is run paintComponent
. Synchronization and volatility in the ui class alone won't fix this.
If you want to add sync, you need to make sure that any image modification is also synced. This needs to be synchronized on some object that is used for each class.
You can avoid some synchronization by putting img
on an instance BufferedImage
or a different instance each time. It should be mutable anyway, and check pathfinderelite's answer if you want to securely install img
on null
.
source to share