#java #concurrency
#java #параллелизм
Вопрос:
в Java у меня есть большой массив строк.
У меня есть один поток, делающий что-то вроде этого:
for (int i=0;i<10000;i ) array[i] = getSomeValue();
У меня есть другой поток, делающий что-то подобное:
for (int i=10000;i<20000;i ) array[i] = getSomeValue();
и другой поток, выполняющий:
for (int i=20000;i<30000;i ) array[i] = getSomeValue();
и так далее.
должен ли я делать что-то особенное для выполнения этой операции?
будет ли это работать?
Я пытаюсь быстрее заполнить этот большой массив, разделив задачу на несколько потоков, но мне интересно, правильно ли это делать.
Я работаю с 64-разрядной машиной с 16 процессорами и всем прочим.
Комментарии:
1. Если вам не повезет, вы можете столкнуться с проблемой параллелизма, если каждый поток одновременно попытается изменить размер массива. Если вы уже знаете общее количество добавляемых элементов, вам следует создать свой массив такого размера.
Ответ №1:
Ваш код будет работать нормально.
Разные части массива независимы друг от друга.
В спецификации говорится:
Одним из соображений реализации для виртуальных машин Java является то, что каждое поле и элемент массива считаются отдельными
Комментарии:
1. не совсем верно: это зависит от того, что он хочет сделать с массивом, когда закончит (нужно убедиться, что обновления будут видны будущим потребителям массива).
2. Это отдельная проблема, jtahlborn.
3. @DJClayworth — почему это отдельная проблема? вопрос в том, корректен ли этот код. для меня «правильный» означает, что вы можете использовать результаты в своем коде. единственный способ использовать результаты кода оператора в его программе — это если он правильно обрабатывает видимость потока. довольно бесполезно инициализировать массив, а затем не иметь возможности прочитать его позже.
4. @DJClayworth это зависит от того, каково определение операционной системы для «работы». «Будет ли это работать» — ответ «да», если после операций записи не возникает проблем с видимостью памяти, и «нет», если они возникают.
Ответ №2:
Это должно работать нормально. Однако, если вы хотите быть уверены, что это безопасно, вы можете заполнить разные массивы в каждом потоке, а затем System.arraycopy() их в один большой массив.
Ответ №3:
вы можете безопасно инициализировать массив с помощью этого кода, однако любой код, который впоследствии должен использовать массив, должен быть корректно синхронизирован с потоками, выполняющими начальные обновления. это может быть так же просто, как «объединить» все потоки инициализации перед использованием массива.
Ответ №4:
Все должно быть в порядке, если getSomeValue()
не имеет побочных эффектов, которые изменяют изменяемое состояние, к которому обращаются несколько потоков. Если в нем нет никаких изменений состояния, то все готово. Вы не будете обращаться к одному и тому же биту памяти ни в одном из циклов.
Будет ли это на самом деле быстрее, зависит от настройки вашего оборудования и реализации потоков.
Ответ №5:
Пока каждый поток работает с определенным сегментом массива, обновления должны быть в порядке. Кроме того, вы определенно можете увидеть повышение производительности, разделив работу. Вероятно, вы захотите проверить уровень повышения, протестировав с другим количеством потоков. Вероятно, следует начинать с 16, поскольку у вас 16 процессоров, и посмотреть, как увеличение и уменьшение влияет на производительность.
Одна из проблем, с которой вы можете столкнуться, связана с видимостью. Я не верю, что элементы массива гарантированно будут видны всем потокам, потому что они не являются изменчивыми. Итак, если к разделам массива необходимо получить доступ нескольким потокам, тогда у вас может возникнуть проблема. Один из способов справиться с этим — использовать AtomicIntegerArray… AtomicReferenceArray.
Ответ №6:
С Java 8 это стало намного проще:
Arrays.parallelSetAll(array, i -> getSomeValue());
Это также должно решить проблемы видимости, упомянутые в других ответах и комментариях.