我正在尝试收集ConcurrentHashMap
中发生的异常的所有计数和异常名称,以便我应该知道此异常发生了多少次。
所以在我的捕获块中,我有地图,它将继续添加异常的名称,并且总计数发生。
下面是我每次出于测试目的which I have modified to always throw SQL Exception
的代码,以便我可以看到异常计数是否准确。
所以某些场景——
1(如果我选择线程数作为10
,任务数作为50
,那么在该映射中,我可以看到该特定字符串的500个异常
2(但是,如果我选择线程数作为40
,选择任务数作为500
,那么我不会在该地图中看到20000
异常,它显示大约19000
.
我的问题是为什么?我在这里做错了什么?
class Task implements Runnable {
public static final AtomicInteger counter_exception = new AtomicInteger(0);
public static ConcurrentHashMap<String, Integer> exceptionMap = new ConcurrentHashMap<String, Integer>();
@Override
public void run() {
try {
//Making a db connection and then executing the SQL-
} catch (SQLException e) {
exceptionMap.put(e.getCause().toString(), counter_exception.incrementAndGet());
} catch (Exception e) {
}
}
}
更新:
如果我有这样的东西 - 我得到 40 个线程和 4000 个 taks 的Null Pointer Exception
。为什么?
catch (SQLException e) {
synchronized(this) {
exceptionMap.put(e.getCause().toString(), counter_exception.incrementAndGet());
}
}
也许正在发生这样的事情:
任务 1-500:捕获异常,准备调用exceptionMap.put
,从传递给所述方法的counter_exception.incrementAndGet()
中获取一个号码
:具有来自原子整数计数器的编号 500,已计划,以便其exceptionMap.put
首先运行
任务 1:具有来自原子整数计数器的数字 1,已计划,以便其exceptionMap.put
最后运行
现在,即使计数器是 500 并且我们有 500 个异常,异常消息也会与 1 相关联,因为它是最近执行的。
错误的方法。
如果您在负载下在应用程序中遇到异常,您应该找到这些异常的原因(或原因(......并修复它们。
计算异常无济于事。 事实上,您可能会通过以下方式使问题变得更糟:
- 使你难以
- 理解你的代码到你添加的额外复杂性, 导致
- 症状发生变化,使识别导致症状的错误变得更加困难,以及
- 由于您计算异常的方式错误而引入新的错误。
好的,那么你能做些什么来使问题更容易发现呢?
- 查看代码库以确保没有压缩异常。
- 查找不记录异常的异常处理程序。
- 查找过于宽泛的异常处理程序;例如,
Exception
、RuntimeException
或Throwable
。 - 查找过早捕获异常的异常处理程序。 如果你有一个意外的异常,你能做的最好的办法是允许它传播"到顶部"并杀死应用程序或(对于 Web 容器(当前请求。 尝试进行更具体的恢复是一个坏主意......因为你的代码不知道异常意味着什么或是什么导致了它。
- 最后,确保所有线程都有一个未捕获的异常处理程序......以便至少记录任何未捕获的异常。
如果在应用程序加载时出现异常(尤其是奇怪的异常(,则可能是您的数据结构/对象/变量由两个或多个线程共享并且未正确同步。 我建议您对代码库进行代码审查,以查找此类问题。
更新
查看更新后的代码,NPE 最可能的原因是 e.getCause()
返回null
;即您的某些异常没有链式的"原因"异常! 这应该是微不足道的处理;即测试e.getCause()
返回的值。
请注意,由于您使用的是 ConcurrentHashMap
,因此不能将null
用作键。 这是明确禁止的 - 参见javadoc。
另一个可能的原因是同步不正确。
synchronized(this) {
exceptionMap.put(e.getCause().toString(),
counter_exception.incrementAndGet());
}
问题是您正在同步this
,而执行相同代码的其他线程将具有不同的this
...因此,您不会与他们同步。
但是,我很确定您实际上不需要在此处与其他线程同步,因为:
- 静态变量在类初始化期间初始化,之后(大概(不赋值,并且
- 对共享对象进行的方法调用是线程安全的。
我会宣布exceptionMap
是final
,并摆脱synchronized
块。
如果您确实需要同步,您应该在这两个静态对象之一上同步,或者可能在Task.class
上同步。