Невозможное исключение NullPointerException - PullRequest
0 голосов
/ 27 сентября 2018

В проекте веб-сервера, написанном несколько лет назад, я иногда получаю очень странное и - на мой взгляд - невозможное NullPointerException.Это происходит в служебном методе регистрации выходных данных на консоли.

Это ошибочная выдержка из метода:

try {
    Encoder encoder = Base64.getEncoder();
    if(logWriter != null) {
        logWriter.write(String.valueOf(System.currentTimeMillis()));
        logWriter.write(" ");
        logWriter.write(String.valueOf(level));
        logWriter.write(" ");
        logWriter.write(encoder.encodeToString(Thread.currentThread().getName().getBytes()).replaceAll("(?:\\r\\n|\\n\\r|\\n|\\r)", ""));
        logWriter.write(" ");
        logWriter.write(encoder.encodeToString(log.getBytes()).replaceAll("(?:\\r\\n|\\n\\r|\\n|\\r)", ""));
        logWriter.write("\r\n");
        logWriter.flush();
    }

    lastWriterActivity = System.currentTimeMillis();
} catch (IOException e) {
    println("Failed to write log to file: " + e, Color.RED);

    try {
        logWriter.close();
    } catch (IOException e1) {
        println("### Possible resource leak; unable to close log writer", Color.RED);
    }

    logWriter = null;
}

, где logWriter - это BufferedWriter.NPE выбрасывается в первый блок catch, вызывающий logWriter.close().

Но: как можно добавить IOException в мой блок try, в то время как мой logWriter равен != null?Base64.getEncoder() не может выдать IOException, и нет другого кода для выполнения.

Это моя трассировка стека:

Exception in thread "connection_0:0:0:0:0:0:0:1@1544725509" java.lang.NullPointerException
at org.jpuzzle.main.Logger.write(Logger.java:347)
at org.jpuzzle.main.Logger.verbose(Logger.java:187)
at org.jpuzzle.protocol.http.HttpRequest.onRequest(HttpRequest.java:1090)
at org.jpuzzle.network.ConnectionListener$Connection.proceed(ConnectionListener.java:438)
at org.jpuzzle.network.ConnectionListener$Connection.run(ConnectionListener.java:408)

Мой метод - synchronized, поэтому взаимное исключение должноне усложняйте, и у меня нет идей, почему это может произойти.

1 Ответ

0 голосов
/ 28 сентября 2018

Краткий ответ:

Перепишите свой блок catch, чтобы избежать NullPointerException

try {
  if(logWriter != null) {
    logWriter.close();
  }
} catch (IOException e1) {
    println("### Possible resource leak; unable to close log writer", Color.RED);
}

Длинный ответ:

Да, logWriter является статическим .... Не препятствует ли синхронизация выполнению метода parralel для метода?

Нет.

Нужны доказательства?

Запустите этот код:

public class NowImFeelingZombified {
  static Object logWriter = new Object();

public static void main(String[] args) {
    final NowImFeelingZombified zombie1 = new NowImFeelingZombified();
    final NowImFeelingZombified zombie2 = new NowImFeelingZombified();

    Thread t1 = new Thread("zombie1 ") {
        @Override
        public void run() {
            zombie1.syncedMethod();
        }
    };
    Thread t2 = new Thread("zombie2 ") {
        @Override
        public void run() {
            zombie2.syncedMethod();
        }
    };
    t1.start();
    t2.start();
}

private synchronized void syncedMethod() {
    try {
        System.out.println(Thread.currentThread().getName() + logWriter.toString());

        Thread.sleep(2000L);

        System.out.println(Thread.currentThread().getName() + logWriter.toString());

        logWriter = null;
    }
    catch (InterruptedException e) {
    }
  }
}

Вывод будет выглядеть примерно так:

zombie1 java.lang.Object@1c34796b
zombie2 java.lang.Object@1c34796b
zombie1 java.lang.Object@1c34796b
Exception in thread "zombie2 " java.lang.NullPointerException
    at NowImFeelingZombified.syncedMethod(NowImFeelingZombified.java:44)

Howsa?

Синхронизация метода получает блокировку для вызывающего метода.Значение t1 не имеет значения для t2 (и, конечно, не для logWriter).

Вот и все.Прочитайте здесь для полной истории .

Кроме того, давным-давно я написал 5-строчный лайнер для этого класса Util, который я продолжаю использовать.Это избавляет меня от написания catch блоков для закрытия Closeables

public static void close(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        }
        catch (IOException e) {
            //logging
        }
    }
}

В любом случае, Java 1.7 представила AutoCloseable (который реализован BufferedWriter), который вы можетехочу исследовать.

...