Reputation: 113
Hi I have a paint method that is drawing an image and I have another method that is constantly modifying the image to be drawn however I experience concurrency exceptions now and again. What is the most efficient way to resolve this please? I know I could use synchronized blocks on the buffered image but then it throws up warnings on synchronizing a none 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);
}
}
Upvotes: 3
Views: 146
Reputation: 18569
There's a thread-safety issue because the contents of the BufferedImage
can be changed while the paintComponent
method is running. Synchronization and volatile in the ui class alone won't fix this.
If you want to add synchronization, then you'll need to make sure any modification of the image is synchronized as well. This needs to be synchronized on some object that's shared between each class involved.
You can avoid some synchronization by making sure to set img
to a copy of the BufferedImage
or a different instance each time. It should still be volatile though, and check pathfinderelite's answer if you want to safely set img
back to null
.
Upvotes: 0
Reputation: 3137
You have a race condition because the value of img
can changed in another thread between the if
conditional and the use of img
in drawImage
if (img != null) { // <-- img can be non-null here
g.drawImage(img, 0, 0, this); // <-- img can now be null
}
You can assign img
to a local variable, then use the local variable in-place of img
in the above code. The local variable will remain constant even if img
is changed to a different object in another thread
final BufferedImage localImg = img; // <-- localImg won't change locally
if (localImg != null) {
g.drawImage(localImg, 0, 0, this);
}
In addition to this, img
should be declared as volatile
so its value is not cached thread-locally; changes in one thread will be seen by others.
private volatile BufferedImage img;
Bear in mind that declaring a variable as volatile
will cause synchronization to occur whenever that variable is accessed; so synchronization is still happening. However, synchronization occurs on the img
reference itself, not the BufferedImage
object to which it refers, so there are no issues here if img
is null
.
Upvotes: 1
Reputation: 6054
you could sync on instance,
private BufferedImage img;
public void modImage(BufferedImage image) {
synchronized(this){
img = image;
}
}
public void paintComponent(Graphics g) {
synchronized(this){
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
}
Upvotes: 1