haz hazzz
haz hazzz

Reputation: 113

Java Concurrent Exception

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

Answers (3)

fgb
fgb

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

pathfinderelite
pathfinderelite

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

Low Flying Pelican
Low Flying Pelican

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

Related Questions