Для несинхронизированного блока кода вызывается метод синхронизации объекта 1s

Обновлено: 21.11.2024

Это краткое руководство будет введением в использование блока synchronized в Java.

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

Фрагмент логики, отмеченный знаком synchronized, становится синхронизированным блоком, что позволяет выполнять только один поток в любой момент времени.

2. Почему синхронизация?

Давайте рассмотрим типичное состояние гонки, когда мы вычисляем сумму, а несколько потоков выполняют метод calculate():

Тогда давайте напишем простой тест:

Мы используем ExecutorService с пулом из 3 потоков для выполнения calculate() 1000 раз.

Если бы мы выполняли это последовательно, ожидаемый результат был бы 1000, но наше многопоточное выполнение почти каждый раз терпит неудачу с несогласованным фактическим результатом:

Конечно, мы не находим этот результат неожиданным.

Простой способ избежать состояния гонки — сделать операцию потокобезопасной с помощью ключевого слова synchronized.

3. Синхронизированное ключевое слово

Мы можем использовать ключевое слово synchronized на разных уровнях:

  • Методы экземпляра
  • Статические методы
  • Блоки кода

Когда мы используем блок synchronized, Java внутренне использует монитор, также известный как блокировка монитора или встроенная блокировка, для обеспечения синхронизации. Эти мониторы привязаны к объекту; следовательно, все синхронизированные блоки одного и того же объекта могут выполняться только одним потоком одновременно.

3.1. Синхронизированные методы экземпляра

Мы можем добавить ключевое слово synchronized в объявление метода, чтобы сделать метод синхронизированным:

Обратите внимание, что как только мы синхронизируем метод, тестовый пример проходит с фактическим результатом как 1000:

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

3.2. Синхронизированные статистическиеc методы

Статические методы синхронизируются так же, как методы экземпляра:

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

3.3. Синхронизированные блоки внутри методов

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

Тогда мы можем протестировать изменение:

Обратите внимание, что мы передали параметр this блоку synchronized. Это объект монитора. Код внутри блока синхронизируется с объектом монитора. Проще говоря, внутри этого блока кода может выполняться только один поток для каждого объекта монитора.

Если бы метод был статическим, мы бы передали имя класса вместо ссылки на объект, и класс был бы монитором для синхронизации блока:

Проверим блок внутри метода static:

3.4. Повторный вход

Блокировка, стоящая за методами и блоками synchronized, является повторно используемой. Это означает, что текущий поток может получать одну и ту же синхронизированную блокировку снова и снова, удерживая ее:

Как показано выше, пока мы находимся в блоке synchronized, мы можем многократно получать одну и ту же блокировку монитора.

4. Заключение

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

if находится в другом классе в другом потоке, проверяя этот код:

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

Метод синхронизации объектов был вызван из несинхронизированного блока кода.

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

3 ответа 3

Метод ожидания из документации.

Снимает блокировку с объекта и блокирует текущий поток до тех пор, пока он снова не получит блокировку.

Вы должны вызывать Wait для объекта только тогда, когда вы держите блокировку,

Похоже, этот код не удерживает блокировку, не так ли?

Я добавляю блокировку (SizeQueueProperty.Locker), но теперь поток никогда не ждет. Всегда продолжайте выполнение. Должен ли быть изменчивым?

Невозможно угадать с предоставленной информацией. Возможно, вам придется сказать, чего вы пытаетесь достичь. Как сказал Ханс, если вам нужно просто регистрировать информацию, вы можете просто добавить событие и зарегистрировать его

Нет, есть еще код. Я хочу защитить выполнение кода, если другие потоки в других классах не завершены

У меня тоже была такая проблема: "Метод синхронизации объектов был вызван из несинхронизированного блока кода". Я вызывал Monitor.Enter(Variable) для типа значения. В моем случае это было целое число. Я решил это, используя экземпляр объекта. RowIndex как целое число, которое я хотел заблокировать. поэтому я изменил свой код на:

А затем в другой теме я бы заблокировал SyncObj, прежде чем вносить какие-либо изменения в RowIndex. Отлично сработало!

Связано

Связанные

Горячие вопросы о сети

Чтобы подписаться на этот RSS-канал, скопируйте и вставьте этот URL-адрес в программу для чтения RSS.

дизайн сайта / логотип © 2022 Stack Exchange Inc; вклады пользователей под лицензией cc by-sa. версия 2022.3.21.41733

Я периодически получаю эту ошибку при работе с ConnectionType.Shared, но не со значением по умолчанию.

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

Текст был успешно обновлен, но возникли следующие ошибки:

jokker23 прокомментировал 10 марта 2020 г.

Также используется
Newtonsoft.Json 12.0.3
RabbitMQ.Client 5.1.1
Microsoft.Extensions.Caching.Memory 3.1.1
Microsoft.Extensions.Configuration.Abstractions 3.1.1
Microsoft.Extensions.Configuration.Json 3.1.1
Microsoft.Extensions.DependencyInjection 3.1.1
Microsoft.Extensions.Logging 3.1.1
Microsoft.Extensions.Logging .Console 3.1.1
NLog.Extensions.Logging 1.6.1
И пользовательская DLL SDK

Есть ли другая интересная информация?

lbnascimento прокомментировал 10 марта 2020 г.

@jokker23 Не могли бы вы предоставить пример кода, который мы можем использовать для воспроизведения проблемы?

lbnascimento прокомментировал 12 марта 2020 г.

@jokker23 Не могли бы вы протестировать текущий мастер? Модель транзакций была полностью переписана.

jokker23 прокомментировал 12 марта 2020 г.

Да, извините, я постараюсь удовлетворить оба запроса как можно скорее. В настоящее время я занят созданием VPN-туннелей для домашних работников, извините за задержку.

прокомментировал alonstar 23 марта 2020 г.

У меня тоже такая же ошибка.

lbnascimento прокомментировал 23 марта 2020 г.

@alonstar Какая у вас версия?

прокомментировал alonstar 23 марта 2020 г.

прокомментировал mwlpeeters 3 апреля 2020 г. •

Обновление 1:
В своем приложении я использую Autofac и настроил его для создания только одного экземпляра базы данных LiteDB (обернутой в провайдера, поэтому на самом деле провайдер был создан один раз, а также внутренняя LiteDB). Строка подключения содержит «Connection=shared».
В этой ситуации я получаю ту же ошибку, что и описанная выше.

Я изменил конфигурацию Autofac, чтобы он всегда создавал нового поставщика (с новым экземпляром LiteDb внутри).
Это решило эту конкретную проблему, но теперь я сталкиваюсь с проблемами использования файла?

Обновление 2:
Ну, теперь я вернул свою конфигурацию Autofac к тому, что у меня было изначально. Итак, один экземпляр провайдера с одним внутренним экземпляром LiteDB. Строка подключения изменена с «Connection=shared» на «Connection=direct», и после нескольких тестов это кажется стабильным.

комментарий nightroman 7 апреля 2020 г.

Я могу надежно воспроизвести это исключение (не обязательно ту же проблему) с
следующим тестом, запущенным в режиме "Отладка теста" (ПКМ \ Отладка). "Выполнить тест", кажется,
обычно работает. Если он по-прежнему не воспроизводится, попробуйте изменить число
в Sleep(50) .

комментарий nightroman 8 апреля 2020 г.

lbnascimento прокомментировал 8 апреля 2020 г.

@nightroman Да, я смог воспроизвести проблему.

Sn3b прокомментировал 4 мая 2020 г.

Есть обновления? Такая же проблема :(

темнава прокомментировала 13 мая 2020 г.

Есть ли какой-либо прогресс в решении этой проблемы или, по крайней мере, есть идеи, в чем причина проблемы?
Я часто получаю эту проблему, и она нарушает работу приложения. Мне нужно будет искать альтернативы как можно скорее, если это не может быть исправлено, поэтому, если вы можете, сообщите мне, какова ситуация.
Спасибо

lbnascimento прокомментировал 13 мая 2020 г.

@jokker23 @alonstar @mwlpeeters @nightroman @Sn3b @temnava
Происходит следующее: Mutex отслеживает, какой поток удерживает блокировку, и позволяет снять блокировку только этому конкретному потоку. Снятие блокировки из другого потока в нашем случае не будет проблемой, поскольку мы используем Mutex только для синхронизации процессов — синхронизация потоков выполняется с помощью других механизмов. Однако Mutex мешает нам это сделать.

Я попытался заменить Mutex на Semaphore, и это полностью устранило эту проблему. Однако это создало собственную проблему: если процесс, который удерживает блокировку, внезапно закрывается, не освобождая ее, все остальные процессы блокируются на неопределенный срок. Этого не происходит с Mutex, потому что он создает AbandonedMutexException , что позволяет нам определить, когда это происходит.

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

темнава прокомментировала 13 мая 2020 г.

Здравствуйте! Большое вам спасибо за быстрый ответ и эту замечательную библиотеку.
Можете ли вы где-нибудь предоставить код решения с реализацией семафора, чтобы я мог использовать его до выхода версии 5.1?
Кстати, есть ли ETA для 5.1, и будут ли эти проблемы возможны в этой новой реализации?
С уважением

lbnascimento прокомментировал 13 мая 2020 г.

Sn3b прокомментировал 13 мая 2020 г. •

@lbnascimento
Из внешнего интерфейса у меня было 2 вызова, которые вызывали разные методы в контроллере: 1 для добавления элемента и 1 для обновления другого.
В конце концов я просто объединил 2 вызова в 1 и создал новый метод в своем контроллере, чтобы операции не выполнялись одновременно.

Редактировать: я использую строку подключения с Connection=shared, поскольку у меня также есть другая служба, которой требуется доступ к той же базе данных.

Прокомментировал MidasLamb 27 мая 2020 г. •

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

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

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

*Редактировать:
Я думаю, что это можно сделать только с помощью операторов «lock», я попытаюсь создать для этого PR. Преимущество в том, что даже если что-то пойдет не так, вы не сохраните блокировку.

[Имя инструктора курса] Отчет об оффшорном счете [Месяц] [Год]

Где бы вы заполнили текст в квадратных скобках выше соответствующими текстовыми данными?

Нет, серьезно, как насчет просто:

Отчет биллинговой системы [номер отчета] [интервал времени год/квартал/месяц/неделя/день]

// Убедитесь, что блокировка снята.
Monitor.Exit(массив);

Если я использую список, ничего плохого не происходит, когда я перевожу его в массив. Спасибо за помощь.

SmartArray3.cs

Код для вызываемого класса:

Объект, который вы передаете в Monitor.Exit, должен быть тем же объектом, который вы передали в Monitor.Enter / Monitor.TryEnter .

Вызывая метод Resize, вы устанавливаете для поля массива новый экземпляр массива. Затем вы передаете этот новый экземпляр в Monitor.Exit , даже если вы никогда не передавали его в Monitor.Enter .

Самым простым решением было бы использование отдельного объекта только для чтения для всех вызовов Monitor:



"Эти люди заглянули глубоко в мою душу и присвоили мне номер в соответствии с порядком, в котором я присоединился".
- Гомер

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

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

Если это так, могу ли я помочь создать класс из этого демонстрационного проекта? Это примерно 1000 строк кода, но я буду рад опубликовать его, если это поможет.

Я не планирую даже просматривать 1000 строк вашего кода — у меня нет времени — но я бы начал с рассмотрения создания UserControl[^], а не универсального класса — они отображаются напрямую. А затем либо встроить DataGridView, либо получить от него. Если вы планируете использовать его на вкладках, я бы также подумал о создании другого пользовательского элемента управления, производного от TabPage, который инкапсулировал пользовательский элемент управления DataGridView, чтобы упростить его использование.

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

Я много использую элементы управления UserControl — они помогают содержать код и упрощают интерфейсы между модулями. Стоит посмотреть!

Спасибо за ответ. Я прочитал статью MS, а также нашел это видео.

Это видео кажется немного более понятным для меня, поскольку я новичок в этом, поскольку я вижу, что это сделано, будет ли это также хорошим представлением того, о чем вы говорите?

Трудно сказать - его голос и "шум ветра" в микрофоне так раздражали, что я не мог столько смотреть!

Серьезно, попробуйте "новый проект" и посмотрите, что получится. Это действительно помогает упростить интерфейсы между модулями и ускорить разработку.

Это означает, что вы не дожили до 4:20, так как примерно в этот момент случайный молоток начинает стучать по стене позади него. или он живет в дешевом мотеле, я не уверен?

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

Читайте также: