Использование потоков в Java для обработки элементов массива

#java #multithreading

#java #многопоточность

Вопрос:

Укажите список целых чисел (положительных и отрицательных), хранящихся в массиве. Как мне проверить, являются ли числа положительными, отрицательными или равными нулю одновременно.

Я подумал о некоторых возможных способах решения этой проблемы:

  • наличие трех разных методов, которые будут вызываться, образуют три разных потока, используя wait и notify . (работает в некоторой степени)
  • наличие одного synchronized метода и трех разных потоков, обращающихся к нему с помощью глобального счетчика. Потоки взаимодействуют друг с другом. (работает в некоторой степени)
  • Предоставление массива в виде фрагментов потокам. Например, поток 1 будет работать с половиной элементов, а поток 2 — с другой половиной. (еще не реализовано, не уверен, что этот подход соответствует определению concurrent правильно).

Пожалуйста, имейте в виду, что это было дано в качестве задания колледжа.

Если бы кто-нибудь дал некоторые идеи или мысли о способах решения этой проблемы, было бы очень полезно.

Редактировать:

Это был точный вопрос. Я попытался немного упростить и приблизиться к нему.

Напишите Java-программу, которая сначала генерирует набор случайных чисел, а затем определяет отрицательные, положительные четные и положительные нечетные числа одновременно.

Комментарии:

1. 3-й — лучший подход.

2. @Amongalen Я тоже об этом думал. Но соответствует ли это определению «одновременно»?

3. Разрешено ли вам использовать либо ConcurrentQueue или Executor ?

4. @chrylis-осторожно оптимистично — Нет, это проблема

5. Если вы хотите получить дополнительную фантазию с подходом 3, попробуйте реализовать это с помощью a ForkJoinPool , который, по сути, специально разработан для этого. Однако я нахожу кодирование очень неинтуитивным, поэтому я предлагаю попробовать его только после завершения фактического назначения.

Ответ №1:

Чтобы объяснить это просто:

  1. Этот подход обычно хорош, когда потоки выполняют различную работу, которая зависит друг от друга. Например, один поток создает некоторые данные, а другой их использует.
  2. synchronized используется для обеспечения того, чтобы только один поток одновременно мог выполнять определенный метод. Обычно используется для доступа к некоторым данным, которые должны быть общими. Например, несколько потоков выполняют свою работу, но затем необходимо обновить какой-то счетчик или что-то в этом роде. Это обновление должно быть синхронизировано.
  3. Делая это, вы гарантируете, что каждый поток имеет свои собственные данные для работы с ним и не должен взаимодействовать с другими потоками. Из-за этого больше времени обработки тратится на выполнение фактической работы и меньше на обмен данными, который не может быть одновременным.

В вашей задаче вам, вероятно, следует использовать 3-й и 2-й методы. Сначала вы разделяете свои данные, обрабатываете их, а затем синхронизированным методом добавляете конечный результат к некоторому счетчику (если только это не делается в совершенно отдельном потоке после того, как все сделано).

Комментарии:

1. Спасибо. Я попробую этот подход.

Ответ №2:

Использование parallelStream

Хотя технически это не использует явные потоки, под капотом используется несколько потоков. Это одновременно. Добавляйте / удаляйте счетчики и просматривайте логику в соответствии с вашими требованиями.

Попробуйте, если хотите

 import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class ConcurrentCounter {

    public static void main(String[] args) {
        final int n = 100;
        final int half = n / 2;
        final Random random = new Random();

        // generate random numbers (use IntStream rather than Random.int(n) to control range to test
        // other generating zero will be difficult. else explicitly add zeros
        List<Integer> numbers = IntStream.range(0, n)
            .map(i -> random.nextInt(n) - half).boxed().collect(Collectors.toList());

        System.out.println(numbers);

        // two counters for -ve,  ve (zero can be counted as total - (-ve    ve)
        AtomicInteger positive = new AtomicInteger(0);
        AtomicInteger negative = new AtomicInteger(0);

        // multithreaded using ForkJoin Pool - beware this can be less performant
        numbers.parallelStream().peek(i -> {
            if (i < 0) {
                negative.incrementAndGet();
            } else if (i > 0) {
                positive.incrementAndGet();
            }
        }).count(); // do not remove this dummy count(), otherwise this will not be executed as this statement will be come no-op

        int zero = n - (positive.get()   negative.get());

        System.out.println("Negative: "   negative.get()   ", Zero: "   zero   ", Positive: "   positive.get());
    }
}