2011-01-07 3 views
7

В моем приложении есть класс, как показано ниже:инициализация класса и синхронизирован метод класса

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 должна поручить завершение инициализации класса, прежде чем вводить свой статический метод в соответствии с моей интуицией, и некоторые из моих экспериментов подтверждают мои предположения. Однако я видел противоположное поведение в другой среде. Может кто-то пролил мне немного света на это?

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

ответ

4

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

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

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

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

+0

Статический инициализатор - это простой метод класса , который вызывается под замком (класс загрузчика). – bestsss

0

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

3

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

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

Гарантируется спецификацией JVM, что это будет сделано в потоковом безопасном режиме. Цитирую JLS section 12.4.2 Detailed Initialization Procedure:

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

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

процедура инициализации класса или интерфейса затем следующим образом:

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

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

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

+0

@Petro: «Поскольку такая же блокировка получена JVM во время инициализации класса», это не точно, потому что в разделе JLS 12.4.2 говорится: «В противном случае запишите тот факт, что инициализация объекта класса в настоящее время выполняется текущий поток и отпустите блокировку объекта Class. ". Таким образом, блокировка класса освобождается до завершения инициализации класса. – nybon

+0

И вы можете проверить это поведение: 1) thread-1 запускает инициализацию класса, которая может занять много времени. 2) thread-2 использует синхронизированный оператор для получения блокировки класса, такой как синхронизированный (MyClass.class). Если время в порядке, поток-2 войдет в блок синхронизированных операторов. – nybon

+0

«откуда вы знаете, что печать происходит до завершения инициализации класса», я добавил инструкцию для печати в конец инициализатора класса, чтобы убедиться в этом. – nybon

2

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

doSomething 
hello 
doSomething 
hello 
hello 
hello 

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

+0

Ahh, я полностью забыл о нескольких загрузчиках классов в одной JVM. Хороший улов! –

+0

Хорошо, я никогда об этом не думаю. Однако, это, похоже, не для моего приложения :( – nybon

Смежные вопросы