假设我有以下类(class):
public class BuggyClass {
private String failField = null;
public void create() {
destroy();
synchronized (this) {
failField = new String("Ou! la la!");
}
}
public void destroy() {
synchronized (this) {
failField = null;
}
}
public long somethingElse() {
if (failField == null) {
return -1;
}
return failField.length();
}
}
很容易看出,在以上代码的多线程执行中,我们可以在somethingElse
中得到一个NullPointerExeption
。例如,它可能是 failField != null
并且在返回之前 failField.length()
destroy
被调用因此使得 failField
到 null
。
我想创建一个多线程程序,在使用 BuggyClass
时能够“抛出”一个 NullPointerException
。我知道,由于该程序是多线程的,所以这可能永远不会发生,但我想应该有一些更好的测试来增加出现异常的可能性。对吧?
我尝试了以下方法:
final BuggyClass bc = new BuggyClass();
final int NUM_OF_INV = 10000000;
int NUM_OF_THREADS = 5;
ExecutorService executor = Executors.newFixedThreadPool(3 * NUM_OF_THREADS);
for (int i = 0; i < (NUM_OF_THREADS); ++i) {
executor.submit(new Runnable() {
public void run() {
for(int i = 0; i< NUM_OF_INV; i++){
bc.create();
}
}
});
}
for (int i = 0; i < (NUM_OF_THREADS); ++i) {
executor.submit(new Runnable() {
public void run() {
for(int i = 0; i< NUM_OF_INV; i++){
bc.destroy();
}}
});
}
for (int i = 0; i < (NUM_OF_THREADS); ++i) {
executor.submit(new Runnable() {
public void run() {
for(int i = 0; i< NUM_OF_INV; i++){
bc.somethingElse();
}}
});
}
executor.shutdown(); executor.awaitTermination(1, TimeUnit.DAYS);
我使用不同的 NUM_OF_INV
和 NUM_OF_THREADS
多次执行上述代码(方法),但从未设法获得 NullPointerException
。
关于如何创建一个测试增加我在不更改 BuggyClass
的情况下获得异常的机会的任何想法?
最佳答案
虽然您的代码中存在数据竞争,但可能无法发现任何由此数据竞争 引起的问题。最有可能的是,JIT 编译器会将方法 somethingElse 转换成如下所示:
public long somethingElse() {
String reg = failField; // load failField into a CPU register
if (reg == null) {
return -1;
}
return reg.length();
}
这意味着,编译器不会在条件之后加载引用failField。并且不可能触发NullPointerException。
更新:我用 GCJ 编译了方法 somethingElse
以查看一些真实的和优化的汇编器输出。它看起来如下:
long long BuggyClass::somethingElse():
movq 8(%rdi), %rdi
testq %rdi, %rdi
je .L14
subq $8, %rsp
call int java::lang::String::length()
cltq
addq $8, %rsp
ret
.L14:
movq $-1, %rax
ret
您可以从这段代码中看到,引用failField 被加载了一次。当然,不能保证所有的实现现在和永远都使用相同的优化。所以,您不应该依赖它。
关于java - 破解并发代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23119711/