Skip to content

Instantly share code, notes, and snippets.

@eribeiro
Created August 12, 2016 01:32
Show Gist options
  • Save eribeiro/4141df2d02c62d7370101bc4349cd8c4 to your computer and use it in GitHub Desktop.
Save eribeiro/4141df2d02c62d7370101bc4349cd8c4 to your computer and use it in GitHub Desktop.
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.*;
public class MapComputeTest
{
public static void main(String[] args)
{
final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
// our dumb collectionWatches
ConcurrentHashMap<String, Set<Integer>> collectionWatches = new ConcurrentHashMap<>();
// our "equivalent" of stateWatchers
final Set<Integer> stateWatchers = new HashSet<>(); // non thread-safe
// COMMENT THE LINE ABOVE AND UNCOMMENT THE LINE BELOW TO FIX THE ISSUE
// no, Collections.synchronizedSet doesn't solve it. :(
// final Set<Integer> stateWatchers = ConcurrentHashMap.newKeySet(); // thread-safe fix
collectionWatches.put("1", stateWatchers);
// pre-populate stateWatchers so that it can have something
// to start looping on
for (int i = 0; i < 10; i++)
stateWatchers.add(i);
// IMPORTANT: this thread modifies stateWatchers concurrently,
// but only does so after 1s so that we have a chance
// of interleaving the operations with the one performed
// by the .compute
executorService.schedule(() -> {
for (int i = 11; i < 20; i++)
{
stateWatchers.add(i);
}
}, 1, TimeUnit.SECONDS);
// FORTUNATELY addAll is very fast, so if I want
// to get the right interleaving then I need to "cheat":
// by overriding the add() method and inserting a little
// delay so that Set insertion is not so fast. ;)
Set<Integer> watchers = new HashSet<Integer>()
{
@Override
public boolean add(Integer o)
{
try
{
TimeUnit.SECONDS.sleep(1);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return super.add(o);
}
};
System.out.println("PLEASE, be patient. Even when it works, has been made slow ;)");
try
{
// the patch solution
collectionWatches.compute("1", (k, v) -> {
if (v != null)
{
watchers.addAll(stateWatchers);
}
return v;
});
// The original code (or almost) with that slow down trick
// watchers = new HashSet<Integer>(stateWatchers)
// {
// @Override
// public boolean add(Integer o)
// {
// try
// {
// TimeUnit.SECONDS.sleep(1);
// }
// catch (InterruptedException e)
// {
// e.printStackTrace();
// }
// return super.add(o);
// }
// };
System.out.println(watchers);
} finally {
executorService.shutdownNow();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment