与えられた:
volatile
遅延初期化シングルトン クラスはsynchronized
、getInstance
. このシングルトンは、ExecutorService
,を介して非同期操作を開始します- タスクには 7 つのタイプがあり、それぞれが一意のキーで識別されます。
- タスクが起動されると、に基づいてキャッシュに保存されます
ConcurrentHashMap
。 - クライアントがタスクを要求すると、キャッシュ内のタスクが完了すると、新しいタスクが起動されてキャッシュされます。実行中の場合、タスクはキャッシュから取得され、クライアントに渡されます。
コードの抜粋を次に示します。
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;
}
1 つの大きな質問と、もう 1 つの小さな質問があります。
getTask
メソッドでロック制御をダブルチェックする必要がありますか? 私ConcurrentHashMap
は読み取り操作がスレッドセーフであることを知っているので、get(key)
スレッドセーフであり、ロックのダブルチェックは必要ないかもしれません (しかし、これについてはまだよくわかりません…)。しかしisDone()
、Future のメソッドはどうでしょうか。synchronized
ブロック内の適切なロック オブジェクトをどのように選択しますか? であってはならないことがわかっているnull
ので、最初に のTaskLauncher.class
オブジェクトを使用しgetInstance()
、次にメソッドtasksCache
で既に初期化されているを使用しますgetTask(String key)
。そして、この選択は実際に重要ですか?