在几年前编写的一个Web服务器项目中,我偶尔会感到很奇怪,并且-在我看来-不可能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;
}


其中logWriterBufferedWriter。 NPE被引发在调用catch的第一个logWriter.close()块中。

但是:当logWriter是IOException时,如何在try块中抛出!= nullBase64.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,因此互斥应该没有任何困难,而且我不知道为什么会发生这种情况。

最佳答案

简短答案:

重写您的catch块,以避免NullPointerException

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


长答案:


  是的,logWriter是静态的。
  不同步是否会阻止方法并行执行方法?


没有。

需要证明吗?

运行此代码:

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)


豪萨

同步方法将获得对该方法的调用者的锁定。含义t1不在乎t2(当然也不在乎logWriter)。

而已。在这里阅读full story

另外,很久以前,我为我一直使用的Util类编写了这样的5衬里。它使我免于编写catch块来关闭Closeables

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


无论如何,Java 1.7引入了您可能想探索的AutoCloseable(由BufferedWriter实现)。

07-26 08:36