繁体   English   中英

想不出异常

[英]Can not figure out an exception

我正在编写简单的代码来将日志异步写入文件,但发现很难找出一个问题。

我在logNodes.removeFirst()得到java.util.NoSuchElementException 如果我检查列表是否为空,这怎么会发生?

如果我非常频繁地登录,则通常会出现此问题。

如果有人能向我解释为什么会发生这种情况,我将不胜感激。

我的代码:

private static class FileLogger extends Thread {
    private File logFile;
    private PrintWriter logWriter;
    private final LinkedList<LogNode> logNodes = new LinkedList<>();

    public FileLogger(Context context) {
        String dateString = (String) DateFormat.format("yyyy-MM-dd_HH:mm:ss", new Date());
        File logsDir = new File(context.getCacheDir(), "logs");

        if (logsDir.exists()) {
            for (File file : logsDir.listFiles()) {
                file.delete();
            }
        }

        try {
            logFile = new File(logsDir, dateString + ".log");
            if (!logFile.exists()) {
                logFile.getParentFile().mkdirs();
                logFile.createNewFile();
            }

            logWriter = new PrintWriter(new FileOutputStream(logFile));
            start();
        } catch (IOException ignored) {
        }
    }

    public void log(Date date, String tag, String msg) {
        if (isAlive()) {
            logNodes.addLast(new LogNode(date, tag, msg));
            synchronized (this) {
                this.notify();
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            if (logNodes.isEmpty()) {
                try {
                    synchronized (this) {
                        this.wait();
                    }
                } catch (InterruptedException e) {
                    logWriter.flush();
                    logWriter.close();
                    return;
                }
            } else {
                LogNode node = logNodes.removeFirst();
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
                logWriter.println(String.format(
                        "%s %s.%s", dateFormat.format(node.date), node.tag, node.msg
                ));
                logWriter.flush();
            }
        }
    }

    private class LogNode {
        final Date date;
        final String tag;
        final String msg;

        public LogNode(Date date, String tag, String msg) {
            this.date = date;
            this.tag = tag;
            this.msg = msg;
        }
    }
}

原因

您没有同步多个log线程。

假设您有线程 1 和线程 2:

  1. thread1 已将 node1 写入队列。
  2. FileLogger在调用isEmpty时注意到了 node1,而 thread2 没有注意到它。
  3. thread2认为这个链表是空的,让链表的第一个和最后一个节点为node2,表示node1已经被覆盖了。
  4. 由于您没有任何其他同步,因此FileLogger可能不会注意到 node2,将抛出NoSuchElementException

解决方案

与其自己实现一个阻塞队列, BlockigQueue尝试使用java.util.concurrent提供的BlockigQueue ,让它为你做同步。

private static class FileLogger extends Thread {
    private File logFile;
    private PrintWriter logWriter;
    private final BlockingQueue<LogNode> logNodes = new LinkedBlockingQueue<>();

    public FileLogger(Context context) {
        String dateString = (String) DateFormat.format("yyyy-MM-dd_HH:mm:ss", new Date());
        File logsDir = new File(context.getCacheDir(), "logs");

        if (logsDir.exists()) {
            for (File file : logsDir.listFiles()) {
                file.delete();
            }
        }

        try {
            logFile = new File(logsDir, dateString + ".log");
            if (!logFile.exists()) {
                logFile.getParentFile().mkdirs();
                logFile.createNewFile();
            }

            logWriter = new PrintWriter(new FileOutputStream(logFile));
            start();
        } catch (IOException ignored) {
        }
    }

    public void log(Date date, String tag, String msg) {
        if (isAlive()) {
            logNodes.add(new LogNode(date, tag, msg));
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                LogNode node = logNodes.take();
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
                logWriter.println(String.format(
                        "%s %s.%s", dateFormat.format(node.date), node.tag, node.msg
                ));
                logWriter.flush();
            } catch (InterruptedException e) {
                logWriter.flush();
                logWriter.close();
                return;
            }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM