Что может вызвать IllegalMonitorStateException изнутри синхронизированного блока? - PullRequest
30 голосов
/ 21 сентября 2011

Сегодня произошло удивительное исключение.Внутри синхронизированного блока мы вызываем wait (), и он выдает IllegalMonitorStateException.Что может вызвать это?

Это происходит в хорошо протестированном коде с открытым исходным кодом: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup#l222

Мы устранили очевидные причины:

  • мы синхронизированы направильная переменная?Да, это muxLock
  • это изменяемая переменная?Нет, muxLock является окончательным
  • мы используем какие-нибудь странные флаги JVM "-XX:", которые могут повлиять на поведение монитора?Нет, но мы запускаем JVM, встроенную в приложение C ++ через JNI.
  • Это странная JVM?Нет, это Sun 1.6.0_25 win / x64 JRE
  • это известная ошибка JVM?Не могу найти что-нибудь подходящее в http://bugs.sun.com/bugdatabase

Итак, я пытаюсь придумать более надуманные объяснения.

  • может быть непостижимым из-заошибка памяти приводит к повреждению состояния монитора?Мы смотрим на это, но пока не видим никаких признаков ошибок памяти.

ОБНОВЛЕНИЕ: (на основе комментариев)

У меня естьтакже проверил из трассировки стека и точки останова, что поток действительно находится в синхронизированном блоке, когда генерируется исключение.Это не тот случай, когда какой-то другой не связанный код генерирует исключение (если только что-то действительно НЕ сбивает с толку Eclipse!)

Ответы [ 4 ]

6 голосов
/ 28 сентября 2011

Единственная подозрительная вещь, которую я вижу, это то, что вы передаете ссылку на 'this' на какой-то другой объект в вашем конструкторе.Возможно ли (на самом деле, не маловероятно), что из-за странного переупорядочения вещей, если какой-то другой поток получит эту ссылку на «this» и вызовет метод, использующий мукслок, все может пойти не так.1002 * Спецификация языка Java довольно специфична в этом отношении:

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

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

Некоторые потенциально полезные обсуждения таких вещей: http://madpropellerhead.com/random/20100328-java-final-fields-are-not-as-final-as-you-may-think

Для некоторыхболее старое, но все же полезное общее обсуждение того, почему публикация 'this' в конструкторе является очень плохой идеей в целом, см., например: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html

2 голосов
/ 29 сентября 2011

http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?r1=1069292&r2=1135026&diff_format=h

здесь я вижу, что тайм-аут был добавлен в последнее время

убедитесь, что startTimeout> 0, иначе вы будете ждать (0) или ждать (-n) это вероятнопричина IllegalMonitorStateException

РЕДАКТИРОВАТЬ: ОК, выше, это катастрофа, но давайте попробуем это:

мы в конструкторе Mux: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup

строка 176 мысоздайте SocketChannelConnectionIO и передайте это после того, как мы сломаемся, и другой поток вступит во владение.

в конструкторе SocketChannelConnectionIO, определенном здесь: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?view=markup в строке 112 мы регистрируемся на канал с новым обработчиком ().

обработчик извлекает что-то на канал и функцию, скажем, функция handleReadReadyвыполнив, мы синхронизируемся на muxLock.

теперь мы все еще в конструкторе, поэтому объект в финале все еще изменчив !!!давайте предположим, что это изменится, теперь у нас есть что-то, ожидающее на другом muxLock

Один на миллион сценарий

РЕДАКТИРОВАТЬ

http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?revision=1135026&view=co

Mux(SocketChannel channel,
    int role, int initialInboundRation, int maxFragmentSize)
    throws IOException
    {
    this.role = role;
    if ((initialInboundRation & ~0x00FFFF00) != 0) {
        throw new IllegalArgumentException(
        "illegal initial inbound ration: " +
        toHexString(initialInboundRation));
    }
    this.initialInboundRation = initialInboundRation;
    this.maxFragmentSize = maxFragmentSize;

    //LINE BELOW IS CAUSING PROBLEM it passes this to SocketChannelConnectionIO
    this.connectionIO = new SocketChannelConnectionIO(this, channel);

    //Lets assume it stops here we are still in constructor
    //and we are not in synchronized block

    directBuffersUseful = true;
    }

теперь в конструкторе SocketChannelConnectionIO http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?revision=1069292&view=co

SocketChannelConnectionIO(Mux mux, SocketChannel channel)
    throws IOException
{
    super(mux);
    channel.configureBlocking(false);
    this.channel = channel;
    //Line below we are registering to the channel with mux that is still mutable
    //this is the line that actually is causing the problem move that to 
    // start() and it should work 
    key = selectionManager.register(channel, new Handler());
}

переместить этот код в start () должно работать key = selectionManager.register(channel, new Handler()); (я предполагаю, что start это executet, когда мы хотим начать обработку)

/**
 * Starts processing connection data.
 */
void start() throws IOException {
    key = selectionManager.register(channel, new Handler());
    key.renewInterestMask(SelectionKey.OP_READ);
}

Но было бы гораздо лучше не создавать SocketChannelConnectionIO в конструкторе mux, но, возможно, где-то после этого то же самое для второго конструктора, создающего StreamConnectionIO с этим

1 голос
/ 29 сентября 2011

Переменные-члены не так окончательны, как хотелось бы. Сначала вы должны поместить синхронизированный объект в конечную локальную переменную. Это не объясняет, почему переменная-член изменена, но если она решает проблему, вы, по крайней мере, знаете, что переменная-член действительно изменена.

1 голос
/ 24 сентября 2011

Ответ на мой взгляд, что это либо ошибка, либо кто-то изменил объект за ссылкой, несмотря на то, что он окончательный. Если вы можете воспроизвести его, я рекомендую установить точку останова чтения / записи в поле muxlock, чтобы увидеть, к ней прикоснулись или нет. Вы могли бы проверить идентификационный хэш-код мультиплексора в первой строке синхронизированного блока и перед тем, как ожидать и уведомлять с соответствующими записями журнала или точками останова. С отражением вы можете изменить окончательные ссылки. Цитата от http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Field.html:

" Если базовое поле является окончательным, метод генерирует исключение IllegalAccessException , если setAccessible (true) не удалось для этого поля и это поле нестатично. Установка конечного поля таким образом имеет смысл только во время десериализации или реконструкции экземпляров классов с пустыми конечными полями, прежде чем они станут доступны для доступа другими частями программы. Использование в любом другом контексте может иметь непредсказуемые последствия, включая случаи, когда другие части программа продолжает использовать исходное значение этого поля. "

Возможно, это ошибка в eclispe, и во время отладки он каким-то образом меняет поле. Воспроизводимо ли это и за пределами Эклиспа? Поместите печатную трассу в улов и посмотрите, что получится.

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