Почему моя реализация 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);
}