Monday, 30 January 2012

Synchronization pitfall

Synchronized method modifier is equivalent to block synchronized(this). Not only invoking of such methods is expensive for virtual machine, but they're also a little bit risky. Object's monitor can be taken not only during method invocation, but also by another object that has a reference to it.
Let's take a look at this example:
public class Resource {
private Object veryImportantObject = new Object();
private final Object mutex = new Object();
public synchronized Object getVeryImportantObject() {
System.out.println("Getting object");
return veryImportantObject;
}
public Object getVeryImportantObjectWithMutex() {
synchronized(mutex) {
System.out.println("Getting object with mutex");
return veryImportantObject;
}
}
}
view raw Resource.java hosted with ❤ by GitHub
Class Resource contains some object and provides synchronized methods for fetching it.
getVeryImportantObject() has synchronized modifier.
getVeryImportantObjectWithMutex() uses synchronized block on private object.

We also have an evil class:
public class Cheater {
private final Resource resource;
public Cheater(Resource resource) {
this.resource = resource;
}
public void blockResource() {
synchronized(resource) {
System.out.println("I'm blocking resource");
while(true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
view raw Cheater.java hosted with ❤ by GitHub
Cheater owns a reference to Resource and it blocks it in blockResource() method.

The class that glues it together:
public class Pitfall {
public static void main(String[] args) throws Exception {
final Resource resource = new Resource();
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
new Cheater(resource).blockResource();
}
});
Thread.sleep(1000);
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
resource.getVeryImportantObject();
}
});
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
resource.getVeryImportantObjectWithMutex();
}
});
}
}
view raw Pitfall.java hosted with ❤ by GitHub
Pitfall invokes 3 threads. The first one uses Cheater to take the monitor of created object of Resource class. Next threads start after 1 second in order to allow the first one performing its task. The second thread tries to get resource object by using method with synchronized modifier, however it never succeeds. The third thread grabs this object by using method with internal synchronized block.
Project Lombok provides an interesting way of not getting into this trap (http://projectlombok.org/features/Synchronized.html)

No comments:

Post a Comment