V6102. Inconsistent synchronization of a field. Consider synchronizing the field on all usages.
The analyzer has detected a field being accessed without synchronization while most previous accesses to this field occurred in synchronized context.
Incomplete synchronization can be a reason for a race condition, where the shared state is being modified by several threads at once, the output depending on the order of threads’ execution. This results in a variety of errors, which show up unexpectedly and often cannot be reproduced when debugging in similar conditions.
In other words, fields must either be synchronized on every usage or remain unsynchronized at all to avoid misleading the programmers who will be maintaining the code later. For this reason, if the analyzer issues a V6102 warning, make sure that all accesses to the field are synchronized.
Here is a simple example from a real project, where accesses to the 'acked' field are synchronized in all cases but one:
public class FixedTupleSpout implements IRichSpout
{
private static final Map<String, Integer> acked = new HashMap<>();
....
public static int getNumAcked(String stormId)
{
synchronized (acked)
{
return get(acked, stormId, 0);
}
}
public static void clear(String stormId)
{
acked.remove(stormId); // <=
....
}
public int getCompleted()
{
synchronized (acked)
{
ackedAmt = acked.get(_id);
}
....
}
public void cleanup()
{
synchronized (acked)
{
acked.remove(_id);
}
....
}
}
Since the access to 'acked' in the 'clear' method is unsynchronized, this field is likely to be accessed from several different threads at once. Since 'acked' is an instance of a thread-unsafe collection HashMap, such access is very likely to corrupt the object’s internal state. To solve this issue, the 'acked.remove(stormId)' expression must be enclosed in a 'synchronized' block:
public class FixedTupleSpout implements IRichSpout
{
private static final Map<String, Integer> acked = new HashMap<>();
....
public static int getNumAcked(String stormId)
{
synchronized (acked)
{
return get(acked, stormId, 0);
}
}
public static void clear(String stormId)
{
synchronized (acked))
{
acked.remove(stormId);
}
....
}
public int getCompleted()
{
synchronized (acked)
{
ackedAmt = acked.get(_id);
}
....
}
public void cleanup()
{
synchronized (acked)
{
acked.remove(_id);
}
....
}
}
This diagnostic is classified as: