Динамически порождает потоки Java для такого случая - PullRequest
1 голос
/ 18 января 2012

Предположим, у меня есть List целых чисел.Каждое int, которое я имею, должно быть умножено на 100.Чтобы сделать это с помощью цикла for, я бы сконструировал что-то вроде следующего:

for(Integer i : numbers){
  i = i*100;
}

Но предположим, что из соображений производительности я хотел одновременно создать поток для каждого числа в numbers и выполнить одинумножение в каждом потоке, возвращающее результат к тому же List.Каков наилучший способ сделать это?

Моя настоящая проблема не так тривиальна, как умножение int s, а скорее задача, что каждая итерация цикла занимает значительное количество времени,и поэтому я хотел бы сделать их все одновременно, чтобы сократить время выполнения.

Ответы [ 7 ]

4 голосов
/ 18 января 2012

Если вы можете использовать Java 7, именно для этой проблемы создается инфраструктура Fork / Join . Если нет, то есть исходный код JSR166 (предложение fork / join) по адресу по этой ссылке .

По сути, вы должны создать задачу для каждого шага (в вашем случае для каждого индекса в массиве) и отправить его в службу, которая может объединять потоки (часть ветвления). Затем вы ждете, пока все завершится и объедините результаты (часть соединения).

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

2 голосов
/ 18 января 2012

Если ваши задачи не зависят друг от друга, вы можете использовать среду Executors. Обратите внимание, что вы получите большую скорость, если создадите не больше потоков, чем у вас есть ядра ЦП.

Пример:

class WorkInstance {
    final int argument;
    final int result;

    WorkInstance(int argument, int result) {
        this.argument = argument;
        this.result = result;
    }

    public String toString() {
        return "WorkInstance{" +
                "argument=" + argument +
                ", result=" + result +
                '}';
    }
}

public class Main {

    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        int numOfCores = 4;
        final ExecutorService executor = Executors.newFixedThreadPool(numOfCores);
        List<Integer> toMultiplyBy100 = Arrays.asList(1, 3, 19);
        List<Future<WorkInstance>> tasks = new ArrayList<Future<WorkInstance>>(toMultiplyBy100.size());
        for (final Integer workInstance : toMultiplyBy100)
            tasks.add(executor.submit(new Callable<WorkInstance>() {
                public WorkInstance call() throws Exception {
                    return new WorkInstance(workInstance, workInstance * 100);
                }
            }));

        for (Future<WorkInstance> result : tasks)
            System.out.println("Result: " + result.get());

        executor.shutdown();
    }
}
1 голос
/ 18 января 2012

Быстрый и грязный способ начать работу - это использовать пул потоков, такой как один, возвращенный Executors.newCachedThreadPool () . Затем создайте задачи, которые реализуют Runnable и submit() их в пул потоков. Также ознакомьтесь с классами и интерфейсами, связанными с этими Javadocs, множеством интересных вещей, которые вы можете попробовать.

См. Главу о параллелизме в Эффективная Java, 2-е изд. , где представлено отличное введение в многопоточную Java.

1 голос
/ 18 января 2012

Создание новой темы для

каждого числа в числах

не очень хорошая идея.Однако использование пула с фиксированным размером, соответствующим количеству ядер / процессоров, может немного повысить производительность.

0 голосов
/ 18 января 2012

Я предполагаю, что вы находитесь на обычном ПК. Максимум N потоков будет одновременно выполняться на вашем компьютере, где N - это число ядер ваших процессоров, поэтому, скорее всего, в диапазоне [1, 4]. Плюс раздор в общем списке.

Но, что еще более важно, стоимость порождения нового потока намного больше, чем стоимость умножения. Можно иметь пул потоков ... но в данном конкретном случае даже не стоит об этом говорить. На самом деле.

0 голосов
/ 18 января 2012

Взгляните на ThreadPoolExecutor и создайте задачу для каждой итерации.Обязательным условием является то, что эти задачи являются независимыми.

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

0 голосов
/ 18 января 2012

Если это единственное приложение на узле, вы должны определить, какое количество потоков завершит работу быстрее всего (max_throughput). Это зависит от того, какой процессор вы используете, насколько JIT может оптимизировать ваш код, поэтому нет общих рекомендаций, кроме измерения.

После этого вы можете распределить задания по пулу рабочих потоков по numbers modulo max_throughput

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...