CyclicBarrier недоразумение
Я попытался запустить пример с CyclicBarrier из одного из руководств: военнослужащий должен заполнять пустые принтеры, когда очередь пустых принтеров равна 3. Но когда я запускаю код, оказывается, что принтеры заполнены 2, 3 или 4 пустыми принтерами в очереди:
Принтер1 пуст
Printer12 пуст
Принтер14 пуст
Принтер13 пуст
Заполнение [Принтер1, Принтер12, Принтер14, Принтер13]
Принтер2 пуст
Принтер7 пуст
Заполнение [Принтер2, Принтер7]
Так что, пример неправильный или мое понимание CyclicBarrier? Я считаю, что очередь должна быть точно размером 3 элемента. Что я должен добавить в код, чтобы это исправить? Заранее спасибо.
Код:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
public class PrinterRecharger {
public static void main(String args[]) {
ServiceMan serviceMan = new ServiceMan(3);
for (int i = 0; i < 15; i++) {
new Thread(new Printer(serviceMan, "Printer" + (i + 1))).start();
}
}
}
class ServiceMan {
private CyclicBarrier queue;
private List<String> inQueue;
public ServiceMan(int hardWorking) {
inQueue = new ArrayList<String>();
queue = new CyclicBarrier(hardWorking, new Runnable() {
@Override
public void run() {
System.out.println("Filling " + inQueue);
inQueue.clear();
}
});
}
public void recharge(String name) {
try {
inQueue.add(name);
queue.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
class Printer implements Runnable {
private String name;
private Random rand;
private ServiceMan serviceMan;
public Printer(ServiceMan serviceMan, String name) {
this.name = name;
this.serviceMan = serviceMan;
this.rand = new Random();
}
public void run() {
try {
while (true) {
TimeUnit.SECONDS.sleep(rand.nextInt(10));
System.out.println(name + " is empty");
serviceMan.recharge(name);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1 ответ
Ваш код является потокобезопасным по нескольким причинам, которые я сразу вижу, и, возможно, по некоторым другим, которые я пропустил. У вас есть данные гонки, а также другие типы условий гонки.
ArrayList
не является потокобезопасным классом, но вы используете его из нескольких потоков без синхронизации. Завернуть список вCollections.synchronizedList()
чтобы увидеть некоторое улучшение.Вам не хватает взаимного исключения между
recharge()
иCyclicBarrier
действие. Поток может добавить элемент в очередь только для того, чтобы очистить его от действия.