Given:
- A lazy initialized singleton class implemented with double-check locking pattern with all the relevant
volatile
andsynchronized
stuff ingetInstance
. This singleton launches asynchronous operations via anExecutorService
, - There are seven type of tasks, each one identified by a unique key,
- When a task is launched, it is stored in a cached based on
ConcurrentHashMap
, - When a client ask for a task, if the task in the cache is done, a new one is launched and cached; if it is running, the task is retrieved from the cache and passed to the client.
Here is a excerpt of the code:
private static volatile TaskLauncher instance;
private ExecutorService threadPool;
private ConcurrentHashMap<String, Future<Object>> tasksCache;
private TaskLauncher() {
threadPool = Executors.newFixedThreadPool(7);
tasksCache = new ConcurrentHashMap<String, Future<Object>>();
}
public static TaskLauncher getInstance() {
if (instance == null) {
synchronized (TaskLauncher.class) {
if (instance == null) {
instance = TaskLauncher();
}
}
}
return instance;
}
public Future<Object> getTask(String key) {
Future<Object> expectedTask = tasksCache.get(key);
if (expectedTask == null || expectedTask.isDone()) {
synchronized (tasksCache) {
if (expectedTask == null || expectedTask.isDone()) {
// Make some stuff to create a new task
expectedTask = [...];
threadPool.execute(expectedTask);
taskCache.put(key, expectedTask);
}
}
}
return expectedTask;
}
I got one major question, and another minor one:
- Do I need to perform double-check locking control in my
getTask
method? I knowConcurrentHashMap
is thread-safe for read operations, so myget(key)
is thread-safe and may not need double-check locking (but yet quite unsure of this…). But what about theisDone()
method of Future? - How do you chose the right lock object in a
synchronized
block? I know it must no benull
, so I use first theTaskLauncher.class
object ingetInstance()
and then thetasksCache
, already initialized, in thegetTask(String key)
method. And has this choice any importance in fact?