Теперь предположим, что вы приобрели блокировку чтения, и после успешного чтения вы хотели изменить значение.
Для этого вам понадобится блокировка записи, которую вы можете получить с помощью метода tryConvertToWriteLock.
Следует отметить, что методы tryConvertToReadLock и tryConvertToWriteLock могут не заблокировать и вернуть штамп как ноль, а это значит, что вызовы этих методов не были успешными.
Метод tryConvertToOptimisticRead освобождает блокировку и возвращает штамп для наблюдения.
Также нужно отметить, что планировщик потоков в случае StampedLock не всегда предпочитает читателей над писателями или наоборот.
Приоритеты потоков
Для потоков мы можем устанавливать приоритеты с помощью метода setPriority класса Thread.
Установив приоритет потока выше, мы сигнализируем, что этот поток должен получить больше процессорного времени, чем потоки с более низким приоритетом.
Установка приоритета потоков не гарантирует, что поток фактически получит больше процессорного времени, так как другие факторы, такие как ожидание ресурсов, могут влиять на производительность потока с более высоким приоритетом.
Также способ, которым базовая операционная система реализует многозадачность, также может влиять на производительность потоков, и поток с более низким приоритетом может в некоторых условиях фактически получать больше времени процессора, чем поток с более высоким приоритетом.
Но в общем, потоки с более высоким приоритетом получат больше процессорного времени, чем потоки с более низким приоритетом.
Существует три константы класса Thread, определяющие приоритет потока MIN_PRIORITY, NORM_PRIORITY и MAX_PRIORITY.
Атомарные переменные
Как мы увидели, ключевой задачей многопоточного программирования является управление доступом параллельных потоков к общим ресурсам.
И мы узнали об использовании блокировок различными способами.
В многопоточном программировании есть такое понятие, как критическая секция.
При многопоточном программировании одновременный доступ к общим ресурсам может привести к неожиданному или ошибочному поведению, поэтому части программы, в которых есть доступ к общему ресурсу, защищаются. Эти защищенные части называются критическими секциями.
Предположим, у меня есть банковский счет.
И мой банковский счет, скажем, содержит 500 долларов.
И у меня есть общий банковский счет с дочерью.
Моя дочь имеет собственный банковский счет, баланс которого составляет 0 долларов.
Дочь просит у меня 100 долларов.
Я перевожу деньги со своего счета на общий счет, а дочка переводит деньги с общего счета на свой счет.
Теперь мы можем смоделировать эту операцию, используя два потока.
Есть поток T1, в котором 100 долларов вычитаются из моего баланса и добавляются к общему балансу.
И есть поток T2, где 100 долларов вычитаются из общего баланса и добавляются к балансу дочери.
Теперь вопрос в том, что может пойти не так, если это будет выполнено как многопоточная программа?
Мы видим, что здесь есть переменная общего баланса, которая читается и записывается двумя потоками.
При этом порядок чтения и записи этой общей переменной может вызвать проблемы.
Например, если чтение с одного потока вклинится между чтением и записью в другом потоке.
Поэтому эта переменная должна быть изолирована и находиться в критической секции.
Критические секции могут быть реализованы с помощью низкоуровневого или высокоуровневого интерфейса программирования.
Предположим, что у нас есть массив элементов.
И доступ к этим элементам осуществляется несколькими потоками.
И эти потоки извлекают элементы из массива и обрабатывают их.
Таким образом, каждый из этих потоков может выполнять цикл с переменной-счетчиком, которая отслеживает текущий элемент.
На каждом этапе цикла эта переменная увеличивается на единицу.
Если у вас несколько потоков, выполняющих этот код, мы уже определили проблемы, которые могут возникнуть.
В гонке потоков могут быть одновременные обращения к общей переменной и некорректная ее запись, и чтение.
Для устранения этой проблемы нужно изолировать эту переменную.
А обработка элементов может продолжаться параллельно.
Таким образом, нужно сделать переменную-счетчик атомарной.
Вместо того, чтобы реализовать это с помощью низкоуровневого программирования, и блокировать весь код, где используется эта переменная, мы можем воспользоваться пакетом java.util.concurrent.atomic, который определяет классы, поддерживающие атомарные операции для одиночных переменных.
И таким образом изолировать только эту переменную, а остальной код оставить параллельным.
При этом вместо синхронизации мы можем просто использовать объекты соответствующих классов.
Атомарные классы гарантируют выполнение определенных операций, таких как увеличение и уменьшение, обновление или добавление значения, потокобезопасным способом.
Пакет java.util.concurrent.atomic также предоставляет класс AtomicReference, который позволяет обновлять ссылку на объект атомарно.
Здесь мы на основе строки создаём объект AtomicReference, который позволяет обновить ссылку на строку атомарно.
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии