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действие. Поток может добавить элемент в очередь только для того, чтобы очистить его от действия.

Другие вопросы по тегам