Инициализация класса и метод синхронизированного класса - PullRequest
7 голосов
/ 07 января 2011

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

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 входит в метод print и выводит строку "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.
  • Используется статическое поле, объявленное T, и ссылка на поле не является константой времени компиляции (§15.28). Ссылки на константы времени компиляции должны быть преобразованы во время компиляции в копию значения константы времени компиляции, поэтому использование такого поля никогда не вызывает инициализацию.

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

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

Ответы [ 4 ]

4 голосов
/ 07 января 2011

Я понимаю, что цитируемый текст состоит в том, что процесс инициализации класса завершен ( будет будет инициализирован ) до того, как будет вызван статический метод, объявленный T .

будет инициализировано означает, что процесс инициализации был запущен и завершился.

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

Глава 12.4.2 JLS описывает подробную процедуру инициализации, которая заботится об инициализации классов в многопоточной среде.

3 голосов
/ 10 января 2011

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

doSomething
hello
doSomething
hello
hello
hello

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

3 голосов
/ 10 января 2011

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

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

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

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

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

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

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

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

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

0 голосов
/ 07 января 2011

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

...