Почему моя реализация producer consumer иногда возвращает верный результат иногда неверный?

Нужно посчитать сколько раз слово "Пьер" встречается в файле. Иногда программа возвращает 1403, что является верным ответом, иногда случайные числа, меньше, чем 1403. При увеличении числа потоков увеличивается вероятность неправильного ответа. В отдельном потоке я добавляю строки в ConcurrentLinkedQueue. В то же время в N потоках я считаю количество вхождений.

1.txt - входной файл: https://drive.google.com/file/d/1DXsHs7fCjq8CgpAy7A79igPVpVw9D2gi/view?usp=drive_link

public class LinesProducer implements Runnable {

    private final ConcurrentLinkedQueue<String> lines;
    private final AtomicBoolean finished;

    public LinesProducer(ConcurrentLinkedQueue<String> lines, AtomicBoolean finished) {
        this.lines = lines;
        this.finished = finished;
    }

    @Override
    public void run() {
        try {
            produce();
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
        finished.set(true);
    }

    private void produce() throws IOException, InterruptedException {
        try (BufferedReader reader = new BufferedReader(new FileReader("src/main/resources/1.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        }
    }
}
public class LinesConsumer implements Runnable {
    private final ConcurrentLinkedQueue<String> lines;
    private final AtomicBoolean producerFinished;
    private int count = 0;
    
    public LinesConsumer(final ConcurrentLinkedQueue<String> lines, AtomicBoolean finished) {
        this.lines = lines;
        this.producerFinished = finished;
    }

    @Override
    public void run() {
        try {
            consume();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void consume() {
        while (!(producerFinished.get() && lines.isEmpty())) {
            if (!lines.isEmpty()) {
                String line = lines.poll();
                count(line);
            }
        }
        PatternAmount.matches.addAndGet(count);
    }

    private void count(String where) {
        Matcher matcher = PatternAmount.PATTERN.matcher(where);
        while (matcher.find()) {
            count++;
        }
    }
}
public class Main {
    private final ConcurrentLinkedQueue<String> lines;
    private final ExecutorService consumers;
    private final int THREAD_AMOUNT = 2;
    private final AtomicBoolean producerFinished;

    public Main() {
        this.producerFinished = new AtomicBoolean(false);
        this.lines = new ConcurrentLinkedQueue<>();
        consumers = Executors.newFixedThreadPool(THREAD_AMOUNT);
    }

    public void start() throws InterruptedException {
        LinesProducer producer = new LinesProducer(lines, producerFinished);
        Thread proudcerThread = new Thread(producer);
        proudcerThread.start();
        for (int i = 0; i < THREAD_AMOUNT; i++) {
            LinesConsumer consumer = new LinesConsumer(lines, producerFinished);
            consumers.submit(consumer);
        }
        consumers.shutdown();
        consumers.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
    }

    public static void main(String[] args) throws InterruptedException {
        Main main = new Main();
        main.start();
        System.out.println(PatternAmount.matches.get());
    }
}
public class PatternAmount {
    public static final Pattern PATTERN = Pattern.compile("\\bПьер\\b", Pattern.UNICODE_CHARACTER_CLASS);
    public static AtomicInteger matches = new AtomicInteger(0);
}

Ответы (1 шт):

Автор решения: Gasan Omarov

Проблема была в поле count класса LinesConsumer. Оно находилось в состоянии гонки. Я сделал ее локальной переменной и все заработало

private void count(String where) {
        int count = 0;
        Matcher matcher = PatternAmount.PATTERN.matcher(where);
        while (matcher.find()) {
            count++;
        }
        PatternAmount.matches.addAndGet(count);
    }
→ Ссылка