Есть ли какой-либо механизм времени компиляции в Java, чтобы попытаться гарантировать, что использование определенного класса всегда синхронизировано? - PullRequest
7 голосов
/ 13 октября 2011

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

public class Foo
{
   public synchronized void abc() { ... }

   public synchronized void def() { ... }

   //etc.
}

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

К сожалению, похоже, что синхронизация на уровне метода не 'больше не собираюсь это сокращать.Вместо этого нам нужно начать синхронизацию на самом Foo.Я не думаю, что что-то вроде java.util.concurrent.AtomicReference тоже может это сократить.Я хочу убедиться, что никто не трогает экземпляр Foo во время выполнения определенной (и, возможно, довольно продолжительной) операции.Так что теперь у нас в коде будут блоки, подобные этому:

Foo foo = new Foo(); //this got created somewhere

//somewhere else entirely
synchronized(foo)
{
   //do operation on foo
   foo.doStuff();
   foo.doOtherStuff();
}

Итак, главное, что меня беспокоит, - это то, что несколько разработчиков и я делимся этим кодом.Foo объекты довольно вездесущи.Поскольку мы больше не получаем бесплатную синхронизацию на уровне метода, мы должны ВСЕГДА помнить, чтобы получить доступ к Foo объекту в блоке synchronized.

Так что мой вопросесть ли в Java какой-либо механизм (встроенный или сторонний), позволяющий мне генерировать предупреждения или ошибки во время компиляции, если к экземпляру Foo обращаются вне блока synchronized?

В идеале это было бы то, что я мог бы сделать с объявлением класса (см. Пример ниже):

@EnsureSynchronized
public class Foo
{
   //etc.
}

Или что-то, что я мог бы сделать, когда я объявил экземпляры Foo (см. Пример ниже):

@EnsureSynchronized
private Foo foo;

Я знаю, действительно ли я хотел бы написать собственное правило FindBugs или PMD, чтобы сделать это, но я надеялся, что нечто подобное уже существует.

Поэтому я прошу васТак что, если бы вы оказались в такой ситуации, как бы вы пытались обеспечить, чтобы к Foo объектам всегда обращались и изменяли только внутри synchronized блоков?

Ответы [ 4 ]

6 голосов
/ 13 октября 2011

Findbugs довольно хорошо находит несогласованную синхронизацию , поэтому, если у вас есть код, который синхронизирует все обращения к объекту, и запускаете findbugs, он должен предупреждать вас о сбояхsync.

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

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

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

Если этого недостаточно, вы всегда можете аннотировать с помощью net.jcip.annotations.NotThreadSafe, который распознает findbugs.

С Глава 10. Аннотации :

FindBugs также поддерживает следующие аннотации:

  • ...
  • net.jcip.annotations.NotThreadSafe
1 голос
/ 13 октября 2011

Если вы хотите, чтобы проверка была во время компиляции, FindBugs и PMD не подойдут.Я хотел бы предложить инструмент обработки аннотаций Java ( APT ).Это позволит вам создать пользовательский процессор аннотаций, который может добавлять проверки в процесс компиляции для использования ваших аннотированных классов и вызывать предупреждения или ошибки компилятора, если ваши требования синхронизации не будут выполнены.На самом деле, вы можете даже использовать его как код для добавления синхронизации во время компиляции, если ее там еще нет.

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

0 голосов
/ 28 октября 2011

Во время выполнения вы можете использовать Thread.holdsLock () .

Задумывались ли вы о наследовании от Foo, например SynchronizedFoo, и об использовании этого в своем коде, в то время как другие могут по-прежнему использовать Foo при необходимости?

0 голосов
/ 13 октября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...