Подтвердить что ты не робот

Как работать с Concurrency перед началом кодирования

Я нахожусь на середине программирования Java-программы, и я нахожусь на том этапе, где я отлаживаю гораздо больше проблем concurrency, чем я хотел бы иметь дело.

Я должен спросить: как вы справляетесь с проблемами concurrency при умственном программировании? В моем случае это для относительно простой игры, но проблемы с потоками продолжают появляться - любое быстрое исправление почти наверняка приводит к новой проблеме.

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

4b9b3361

Ответ 1

Concurrency сводится к управлению общим состоянием.

"Все проблемы concurrency сводятся к координируя доступ к изменяемому состоянию. Чем менее изменчивое состояние, тем легче является обеспечение безопасности потоков". - Java concurrency на практике

Таким образом, вы должны задать себе вопрос:

  • Каковы неотъемлемые общие данные, которые потребуются моему приложению?
  • Когда поток может работать с моментальным снимком данных, то есть мгновенная работа над клоном общих данных?
  • Могу ли я идентифицировать известный шаблон и использовать абстракцию более высокого уровня, а не блокировки на нижнем уровне и координацию потоков, например. очереди, исполнитель и т.д.?
  • Подумайте о глобальной схеме блокировки, чтобы избежать тупиковой ситуации и иметь последовательное получение блокировок.

Самый простой способ управления общим состоянием - сериализовать каждое действие. Однако этот крупнозернистый подход приводит к высокой конкуренции и низкой производительности. Управление concurrency можно увидеть как упражнение по оптимизации, в котором вы пытаетесь уменьшить конфликт. Итак, следующие вопросы:

  • Каким будет самый простой подход?
  • Какой простой выбор я могу сделать, чтобы уменьшить конфликт (возможно, с мелкозернистой блокировкой) и повысить производительность без чрезмерного усложнения решения?
  • Когда я буду слишком оштрафован, то есть, сложность не стоит увеличения производительности?

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

  • Где можно ослабить несколько ограничений и принять, что иногда вещи не будут на 100% правильными (например, счетчик)?
  • Могу ли я быть оптимистом и бороться с конфликтами, только когда происходят параллельные изменения (например, используя временную метку и логику повтора - что делает TM)?

Обратите внимание, что я никогда не работал над игрой, только на стороне сервера корпоративных приложений. Я могу представить, что это может быть совсем другое.

Ответ 2

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

Я должен добавить, что некоторые вещи, которые следует учитывать в ваших будущих начинаниях, - это модели STM и Actor. Оба этих подхода к concurrency демонстрируют очень хороший прогресс. Хотя для каждого из них есть накладные расходы, в зависимости от характера вашей программы, которая может не быть проблемой.

Edit:

Вот несколько ссылок на некоторые библиотеки, которые вы могли бы использовать в своем следующем проекте. Там Deuce STM, который, как следует из названия, является реализацией STM для Java. Тогда есть ActorFoundry, который, как следует из названия, является моделью Actor для Java. Однако я не могу помочь подключиться к Scala с его встроенной моделью Actor.

Ответ 3

Прочитайте concurrency, или, еще лучше, возьмите курс на уровне бакалавриата по параллельному программированию, если вы все еще в колледже. См. Учебники по Java: Урок: Concurrency. Одна известная книга для Java concurrency - это Java concurrency на практике. Java так много встроена в структуру для решения проблем concurrency, включая параллельные коллекции и synchronized.

Java concurrency на практике http://ecx.images-amazon.com/images/I/51Hx%2Bg4Q6QL._BO2,204,203,200-76_AA240_SH20_OU01_.jpg

Ответ 4

Чем меньше потоков у вас есть, тем меньшее состояние они разделяют, и чем проще их структура взаимодействия в этом общем состоянии, тем проще будет ваша жизнь.

Вы говорите, что списки бросают ConcurrentModificationException. Я полагаю, что ваши списки заняты отдельными потоками. Итак, первое, что вы должны спросить себя, - это то, что нужно. Не возможно ли, чтобы второй поток работал с копией списка?

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

Также имейте в виду, что если список является общим состоянием, равно как и его содержимое, поэтому, если вы намерены обойти блокировку, скопировав список, обязательно выполните глубокую копию или убедитесь, что объекты, содержащиеся в списке, сами потоки безопасны.

Ответ 5

Возможно, многопоточность вашего приложения может быть красной селедкой в ​​отношении упомянутых вами ConcurrentModificationExceptions: есть другие способы, с помощью которых вы можете получить исключение ConcurrentModificationException, которое не обязательно связано с несколькими потоками. Рассмотрим следующее:

List<Item> items = new ArrayList<Item>();

//... some code adding items to the list

for (Item item : items) {
    if(item.isTheOneIWantToRemove()) {
        items.remove(item); //This will result in a ConcurrentModificationException
    }
}

Изменение цикла for в цикле с помощью итератора или увеличение значения индекса решает проблему:

for (Iterator<String> it = items.iterator(); it.hasNext();) {
    if(item.isTheOneIWantToRemove()) {
        it.remove(); //No exception thrown
    }
}

или

for (int i = 0; i < items.size(); i++) {
    if(item.isTheOneIWantToRemove()) {
        items.remove(items.get(i)); //No exception thrown
    }
}

Ответ 6

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

Как отмечали другие, сокращение количества потоков в вашем дизайне до абсолютного минимума, которое оно должно работать правильно, также поможет.

Ответ 7

Это зависит от ваших потоков. Обычно программы имеют основной поток, который делает потоки мышления и рабочего процесса для выполнения параллельных задач (таймеры, обработка длинных вычислений в графическом интерфейсе и т.д.). Но ваше приложение может быть другим - это зависит от вашего дизайна. Для чего вы используете потоки? Какие блокировки вы должны защищать общие хранилища данных? Если вы используете несколько блокировок, у вас есть один заказ, в котором вы блокируете, чтобы предотвратить взаимоблокировки?

Ответ 8

  • Попробуйте использовать коллекции из пакета java.util.concurrent или даже лучшие неизменные коллекции из Коллекций Google.
  • Читайте об использовании синхронизированных блоков

Ответ 9

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

http://javatip.com/2010/07/core-java/concurrency/thread-safe-without-synchronization