Обработка InterruptedException при ожидании сигнала выхода (ошибка в Android?)

Я наткнулся на приведенный ниже код, и мне интересно, делает ли он именно то, что я думаю:

synchronized(sObject) {
    mShouldExit = true;   
    sObject.notifyAll()    
    while (!mExited) {
      try {
           sObject.wait();
        } catch (InterruptedException ex) {
           Thread.currentThread().interrupt();
        }
     }
}

О контексте: есть другой поток, который проверяет mShouldExit(внутри монитора sObject) и в этом случае завершается.

Мне кажется, это неправильный шаблон. Если произойдет прерывание, он снова установит статус прерывания, поэтому, когда он вернется к sObject.wait(), появится еще одно InterruptedException и т. д. и т. д. и т. д. Следовательно, он никогда не сможет перейти в действительное состояние ожидания. ( sObject.wait()), то есть он никогда не освободит монитор sObject. Это может привести к бесконечному циклу, так как другой поток не может установить для mExiting значение true, потому что он никогда не сможет войти в монитор sObject. (Поэтому я думаю, что вызов interrupt()является ошибкой, его нельзя здесь использовать.) Я что-то упустил?

Обратите внимание, что фрагмент кода является частью официального исходного кода платформы Android.

ОБНОВЛЕНИЕ:на самом деле ситуация еще хуже, потому что тот же шаблон используется в Android, когда начинается рендеринг GL. Официальный исходный код GLSurfaceView.GLThread.surfaceCreated():

   public void surfaceCreated() {
        synchronized(sGLThreadManager) {
            if (LOG_THREADS) {
                Log.i("GLThread", "surfaceCreated tid=" + getId());
            }
            mHasSurface = true;
            sGLThreadManager.notifyAll();
            while((mWaitingForSurface) && (!mExited)) {
                try {
                    sGLThreadManager.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

Вы можете воспроизвести ошибку аналогичным образом: убедитесь, что ваш UI-поток все еще имеет флаг прерванного состояния, затем добавьте свой GLSurfaceView и запустите визуализацию GL (через setRenderer(.. .), но на некоторых устройствах убедитесь, что ваш GLSurfaceView имеет статус Visibility.VISIBLE, иначе рендеринг не начнется).

Если вы выполните описанные выше шаги, ваш поток пользовательского интерфейса зациклится, потому что приведенный выше код будет продолжать генерировать InterruptedException(из-за wait()) и поэтому поток GL никогда не сможет установить mWaitingForSurfaceв false.

Согласно моим тестам, похоже, что такой бесконечный цикл также приведет к бесконечной последовательности сборки мусора GC_CONCURRENT(или, по крайней мере, к таким сообщениям в logcat). Интересно, у кого-то ранее была неизвестная плохо определенная проблема в stackoverflow, которая может быть связана: Как решить проблему освобождения GC_concurrent?

Возможно ли, что флаг прерывания его потока пользовательского интерфейса был установлен в значение true, и он использовал GLSurfaceView для карты, которую он упоминает? Просто предположение, возможный сценарий.

9
задан Community 23 May 2017 в 12:31
поделиться