以下のコードについて言及している選択肢のうち、正しいものを1つ選んでください。
class CurryMadeTask implements Runnable {
private final int teamNumber;
private final ReentrantLock[] ingredientLocks;
private final List<String> completedTeams;
private static final String[] INGREDIENTS = {"たまねぎ", "にんじん", "じゃがいも", "牛肉"};
private static final int[] COOKING_TIMES = {2, 1, 4, 3};
public CurryMadeTask(int teamNumber, ReentrantLock[] ingredientLocks, List<String> completedTeams) {
this.teamNumber = teamNumber;
this.ingredientLocks = ingredientLocks;
this.completedTeams = completedTeams;
}
@Override
public void run() {
try {
for(int i = 0; i < ingredientLocks.length; i++) {
ingredientLocks[i].lock();
try {
System.out.println("チーム" + teamNumber + "が" + INGREDIENTS[i] + "を調理します");
Thread.sleep(COOKING_TIMES[i] * 1000);
} finally {
ingredientLocks[i].unlock();
}
}
System.out.println("チーム" + teamNumber + "がカレーのルーを入れます");
Thread.sleep(5 * 1000);
System.out.println("チーム" + teamNumber + "カレーができました");
synchronized(completedTeams) {
completedTeams.add("チーム" + teamNumber);
}
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public class CurryMade{
public static void main(String[] args) throws InterruptedException {
final int numTeams = 3;
final ReentrantLock[] ingredientLocks = new ReentrantLock[4];
for(int i = 0; i < ingredientLocks.length; i++) {
ingredientLocks[i] = new ReentrantLock();
}
List<String> completedTeams = Collections.synchronizedList(new ArrayList<>());
ExecutorService executor = Executors.newFixedThreadPool(numTeams);
for(int i = 1; i <= numTeams; i++) {
executor.execute(new CurryMadeTask(i, ingredientLocks, completedTeams));
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.HOURS);
System.out.println("-------順位-------");
completedTeams.forEach(System.out::println);
}
}
選択肢
- 最後に出力される順位は必ずチーム1、 チーム2、チーム3の順番になる
- 「カレーができました。」という出力は、カレーの材料に関する出力の後になる
- あるチームがカレーのルーを入れている時、別のチームはカレーのルーを使うことができない
- ReentrantLockを使用しない場合、順位は一定になる
- ReentrantLockを使用することで、すべての素材の調理は一定の順序で進行し、各チームが順番に取り組むように制御される。
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
正解
5
「一定の順序で進行」と「順番に取り組む」という表現により、すべての素材が順次一斉に調理されるかのように誤解されるかもしれないですが正解になります。
各素材が個別にロックされ、同じ素材に対してのみ制御が働きます。
CurryMadeTask
クラスのrun()
メソッド内で各素材のロックが取得され、調理が順番に行われることを示しています。これにより、各チームが素材を取り合うことなく順序よく調理を行うことが保証されます。
不正解
1: 最後に出力される順位は必ずチーム1、 チーム2、チーム3の順番になる
ReentrantLockによって素材の使用が制限されるため、チームごとの調理順序は毎回変わり、順位は固定されません。
2: 「カレーができました。」という出力は、カレーの材料に関する出力の後になる
例えばチーム1がカレー作りできた後にも、他のチームがカレーのルーを入れる場面はある。
実行例
チーム1がたまねぎを調理します
チーム1がにんじんを調理します
チーム2がたまねぎを調理します
チーム1がじゃがいもを調理します
チーム2がにんじんを調理します
チーム3がたまねぎを調理します
チーム3がにんじんを調理します
チーム1が牛肉を調理します
チーム2がじゃがいもを調理します
チーム1がカレーのルーを入れます
チーム2が牛肉を調理します
チーム3がじゃがいもを調理します
チーム2がカレーのルーを入れます
チーム3が牛肉を調理します
チーム1カレーができました
チーム3がカレーのルーを入れます
チーム2カレーができました
チーム3カレーができました
3: あるチームがカレーのルーを入れている時、別のチームはカレーのルーを使うことができない
カレーのルーを入れる操作はReentrantLockで制御されていないため、複数のチームが同時にルーを入れることは可能です。
4: ReentrantLockを使用しない場合、順位は一定になる
ReentrantLockを使用しない場合、各チームが同時に素材を調理しようとして競合が発生しません。
また、Executors.newFixedThreadPool
により同時に実行されるタスク数が制限されているため、各チームの素材の調理順序はランダムに変わります。その結果、順位は毎回異なります。