[java] Инициализация класса и метод синхронизированного класса


1 Answers

Выполнение статических блоков, считающихся частью инициализации класса:

Инициализация класса состоит в выполнении его статических инициализаторов и инициализаторов для статических полей (переменных класса), объявленных в классе ...

По спецификации JVM гарантируется, что это будет сделано в потоковом безопасном режиме. Процитировать JLS-раздел 12.4.2. Подробную процедуру инициализации :

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

Более подробно это реализовано путем приобретения блокировки объекта Class:

Процедура инициализации класса или интерфейса следующая:

  1. Синхронизировать (§14.19) с объектом класса, который представляет класс или интерфейс для инициализации

Ваш метод static synchronized и требует блокировки объекта класса. Поскольку одна и та же блокировка получена JVM во время инициализации класса, невозможно, чтобы один поток инициализировал класс и для другого выполнял static synchronized метод на нем. Все цитаты взяты из JLS. Надеюсь, это полезно. Кстати, откуда вы знаете, что печать происходит до завершения инициализации класса?

EDIT: На самом деле я ошибаюсь в предположении, что только static synchronized не может выполняться параллельно с инициализацией класса. Любые методы класса не могут быть выполнены до завершения инициализации класса.

Question

В моем приложении есть класс, как показано ниже:

public class Client {
    public synchronized static print() {
        System.out.println("hello");
    }

    static {
        doSomething(); // which will take some time to complete
    }
}

Этот класс будет использоваться в многопоточной среде, многие потоки могут одновременно вызвать метод Client.print (). Интересно, есть ли вероятность того, что thread-1 запускает инициализацию класса, и до завершения инициализации класса thread-2 входит в метод печати и печатает строку «hello»?

Я вижу это поведение в производственной системе (64-разрядная версия JVM + Windows 2008R2), однако я не могу воспроизвести это поведение с помощью простой программы в любых средах.

В спецификации языка Java, раздел 12.4.1 (http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html), говорится:

Класс или тип интерфейса T будут инициализированы непосредственно перед первым вхождением любого из следующих:

  • T - класс, и создается экземпляр T.
  • T - класс, и статический метод, объявленный T, вызывается.
  • Статическое поле, объявленное T, назначается.
  • Статическое поле, объявленное T, используется, а ссылка на поле не является константой времени компиляции (§15.28). Ссылки на константы времени компиляции должны быть разрешены во время компиляции до копии значения постоянной времени компиляции, поэтому использование такого поля никогда не вызывает инициализации.

В соответствии с этим параграфом инициализация класса будет выполняться до вызова статического метода, однако неясно, нужно ли завершить инициализацию класса до вызова статического метода. JVM должна поручить завершение инициализации класса, прежде чем вводить свой статический метод в соответствии с моей интуицией, и некоторые из моих экспериментов подтверждают мои предположения. Однако я видел противоположное поведение в другой среде. Может кто-то пролил мне немного света на это?

Любая помощь приветствуется, спасибо.




Если код запущен в некоторых контейнерах, таких как Servlet, вы можете инициализировать его в жизненном цикле контейнера.




Related