We are working in an OSGi context and are getting instances of our services via this idiom which includes double-checked locking:
private volatile ServiceTracker<IMyService, IMyService> serviceTrackerField = null;
public IMyService getMyService()
{
// double-checked locking
ServiceTracker<IMyService, IMyService> localTracker = this.serviceTrackerField;
if (localTracker == null)
{
synchronized (this)
{
localTracker = this.serviceTrackerField;
if (localTracker == null)
{
localTracker = new ServiceTracker<>(getBundle().getBundleContext(), IMyService.class, null);
localTracker.open();
System.out.println("TRACKER = " + localTracker); // X
this.serviceTrackerField = localTracker;
}
}
}
return serviceTracker.waitForService(MY_WAIT_TIMEOUT);
}
I thought I had understood the basic principles (copy to local, use volatile to guarantee order), but still it can happen that the print
in line X prints two different instances of the service tracker. (It doesn't seem to be a problem - the service is acquired correctly; but obviously, we are creating more service trackers than we'd need to and that still gives me a bad feeling).
We are currently switching to a different solution, but I still want to understand how this can happen. Can anyone explain what could go wrong? (JRE is Oracle JRE 1.8.0_141 if it matters).
Edit: the Code above is in a bundle activator (actually in Eclipse/Equinox context). So it can be assumed there is only one instance of the class.