非常奇怪:android:java.util.logging.Logger.removeHandler抛出ConcurrentModificationException

时间:2016-07-08 08:30:10

标签: java android

注意!!!

您在发布的代码中看到的for-each迭代器不会抛出此异常!!!

我在我的Android应用程序中遇到了一个非常奇怪的ConcurrentModificationException。

import java.util.logging.Handler;
import java.util.logging.Logger;
public MyApp extends Application {
    private static final Logger rootLogger = Logger.getLogger("");
    @override
    public void onCreate() {
        super.onCreate();
        ......
        Handler[] handlers = rootLogger.getHandlers();
        for (Handler handler : handlers) {
            rootLogger.removeHandler(handler);
        }
        rootLogger.addHandler(rootHandler);
        rootLogger.setLevel(fileLogLevel);
    }
}

在非常偶然的情况下,当我通过点击桌面图标启动应用时,会出现例外情况:

Caused by: java.util.ConcurrentModificationException
E/AndroidRuntime(26111): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
E/AndroidRuntime(26111): at java.util.logging.Logger.updateDalvikLogHandler(Logger.java:232)
E/AndroidRuntime(26111): at java.util.logging.Logger.updateDalvikLogHandler(Logger.java:233)
E/AndroidRuntime(26111): at java.util.logging.Logger.removeHandler(Logger.java:486)

注意!!!

您在发布的代码中看到的for-each迭代器不会抛出此异常!!!

Application.onCreate()没有并发访问权。

这里是您参考的源代码:https://android.googlesource.com/platform/libcore/+/android-6.0.1_r54/luni/src/main/java/java/util/logging/

我强烈建议您阅读源代码,这必须有助于理解我解释的内容。

===这是我为异常研究的内容===

让我们在第一个代码块中到达Handler[] handlers = rootLogger.getHandlers();Logger.getHandlers()的实施是:

public Handler[] getHandlers() {
    return handlers.toArray(EMPTY_HANDLERS_ARRAY);
}

Logger.removeHandler(Handler)的核心部分是,在Logger.java中:

private final List<Handler> handlers = new CopyOnWriteArrayList<Handler>();
public void removeHandler(Handler handler) {
    ......
    this.handlers.remove(handler);
    updateDalvikLogHandler();
}

所以我们可以看到第一个代码块中发布的logger.removeHandler(handler);没有抛出异常。

让我们深入了解异常追踪。例外是Logger.java:

for (Logger logger : children) { // this line in crash trace
    logger.updateDalvikLogHandler();
}

,这是异常跟踪的最后一个条目,在ArrayList.java中:

public E next() {
    ArrayList<E> ourList = ArrayList.this;
    int rem = remaining;
    if (ourList.modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    ......
}

异常的确切原因是在调用Logger.children时某处修改了for (Logger logger : children)

Logger.java的评论说它的所有方法都是线程安全的。

===结束我的解释,开始我的问题===

我完全混淆Logger.children中发生的事件LogManager。但是我的代码没有调用meteor npm install

我相信我需要有人帮助我澄清我的想法,并帮助找到错误。

非常感谢阅读长篇文章...

2 个答案:

答案 0 :(得分:0)

我无法发表评论,所以我会将其作为答案发布。

如果您对for-each代码发表评论,是否会引发异常?

我怀疑抛出异常是因为您在代码中的其他位置执行logger后修改logger = rootLogger并且系统也在修改它。

答案 1 :(得分:0)

java.util.logging.Logger.java中:

/**
 * Child loggers. Should be accessed only while synchronized on {@code
 * LogManager.getLogManager()}.
 */
final List<Logger> children = new ArrayList<Logger>();

显然Logger.updateDalvikLogHandler()中的子级循环被调用而无需同步。

for (Logger logger : children) {
    logger.updateDalvikLogHandler();
}

您可能正在其他类中创建记录器,例如

static final LOG = Logger.getLogger(MyClass.class.getName())

依次更新Logger.children属性。

可以从主线程以外的其他位置调用。当您从其他线程使用MyClass时,静态字段初始化将创建新的记录器,该记录器将更新根记录器的子代属性。

在这种情况下,我认为在不同步到LogManager的情况下使用children属性是Logger中的错误。

解决方法是在同步块中调用日志记录初始化

synchronized (LogManager.getLogManager()) {
    Handler[] handlers = rootLogger.getHandlers();
    for (Handler handler : handlers) {
        rootLogger.removeHandler(handler);
    }
    rootLogger.addHandler(rootHandler);
    rootLogger.setLevel(fileLogLevel);
}