Любая программа, в которой не может изменяться ни один изменяемый аспект состояния программы более чем одним потоком, будет тривиально поточно-безопасным, так как каждый поток также может быть его собственной отдельной программой. Однако полезная многопоточность, как правило, требует взаимодействия между потоками, что подразумевает существование некоторого изменчивого общего состояния.
Ключом к безопасной и эффективной многопоточности является включение изменчивости на правый «уровень проектирования». В идеале каждый аспект состояния программы должен быть представлен одной неизменяемой (*), изменяемой ссылкой на объект, наблюдаемое состояние которого неизменено. Только один поток за раз может попытаться изменить состояние, представленное определенной изменяемой ссылкой. Эффективная многопоточность требует, чтобы «изменяемый уровень» в состоянии программы был достаточно низким, чтобы разные потоки могли использовать разные части. Например, если у вас есть неизменяемая структура данных AllCustomers
, и два потока одновременно пытались изменить разных клиентов, каждая из них создавала версию структуры данных AllCustomers
, которая включала бы свои собственные изменения, но не изменения в другом потоке. Не хорошо. Если AllCustomers
были изменчивым массивом объектов CustomerState
, однако один поток мог бы работать на AllCustomers[4]
, а другой работал на AllCustomers[9]
без помех.
(*) Корневой путь должен существовать, когда аспект состояния становится релевантным и не должен меняться, пока доступ имеет значение. Например, можно было бы сконструировать AddOnlyList<thing>
, которые содержат thing[][]
под названием Arr
, который был инициализирован до размера 32. Когда добавляется первое, Arr[0]
будет инициализироваться с использованием CompareExchange
в массив из 16 thing
. Следующие 15 вещей пойдут в этом массиве. Когда добавляется 17-я вещь, Arr[1]
будет инициализирован с использованием CompareExchange
массиву размером 32 (который будет содержать новый элемент и 31 элемент после него). Когда добавляется 49-я штука, Arr[2]
будет инициализирован для 64 элементов. Обратите внимание, что в то время как thing
сам и массивы, содержащиеся в нем, не будут полностью неизменными, только самый первый доступ к любому элементу будет писать, и как только Arr[x][y]
будет ссылаться на что-то, он будет продолжать делать это до тех пор, пока существует Arr
.
Пример чего? –
@ Erick Robertson - пример, который решает каждую проблему когда-либо! :) – willcodejavaforfood
@willcodejavaforfood - Был бы признателен, если может предоставить этот пример. Благодаря! –