Блокировка потока при синхронизированном методе в приложении на основе OSGi (Караф) - PullRequest
0 голосов
/ 17 января 2020

У нас есть приложение на основе OSGi, которое предлагает центральный интерфейс сервиса для других пакетов, один метод реализации сервиса синхронизирован:

MyServiceImpl implements Service {

    @Override
    public synchronized doSomething() {

    }

}

Несколько потоков получают доступ к этой услуге (например, верблюжий маршрут и вызов веб-службы) и одновременный вызов метода doSomething (). Так что здесь ничего особенного, Java должен синхронизировать вызов метода здесь.

В любом случае, мы сталкиваемся с проблемой, что наша система застревает: дамп потока говорит нам, что какой-то поток находится в состоянии «заблокирован», потому что другой поток удерживает блокировку объекта реализации службы. Каждый поток пытается вызвать метод doSomething (), а один поток ожидает вечно, что блокировка снята.

Мой вопрос: как это может произойти, здесь нет ничего особенного, я не могу понять, почему замок не снимается!?

1 Ответ

2 голосов
/ 18 января 2020

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

Однако самый простой случай тупика - это когда у вас есть блокировка A и блокировка B. Поток T1 получает блокировку A, в то время как одновременно поток T2 получает блокировку B. T1 выполняет некоторую работу и пытается получить блокировку B. Однако B удерживается T2, поэтому он блокируется. T2 выполняет некоторую работу и пытается получить блокировку B. Он блокируется T1, поэтому блоки T2.

T1 ожидает A, который удерживается T2, и T2 ждет B, который удерживается T1. Так что ни один из них не может добиться прогресса и снять блокировку.

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

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

По этой причине методы synchronized on были плохой идеей. Synchronized - это низкоуровневая конструкция; Вы должны использовать его только для очень маленьких блоков кода, которые не вызывают внешний код . Т.е. вы обновляете структуру данных, но не вызываете. Когда вы находитесь в синхронизированном блоке и звоните в другие службы, вы играете в русскую рулетку. Если вам нужно увеличить время, используйте классы в java.util.concurrency, которые разрешают тайм-ауты. Тайм-ауты - самое простое решение для взаимоблокировок.

Существует много шаблонов для правильного использования синхронизированных. Тем не менее, это очень сложная область. Все, что я знаю о блокировке (и многое другое), я узнал из Обработка транзакций . Хорошей книгой о Java параллелизме является Java Параллелизм на практике .

...