我正在尝试测试,出现多线程并发问题。
我期望的是结果应该小于0(并非总是但有时),因为它不同步。 但下面的测试失败了。
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.Test;
class OptionTest {
static class Option {
private int quantity;
public Option(int quantity) {
this.quantity = quantity;
}
public int getQuantity() {
return quantity;
}
public boolean subtract(int quantity){
if(quantity >= this.quantity){
return false;
}
// System.out.println("quantity = " + quantity);
this.quantity -= quantity;
return true;
}
}
@Test
void asyncSubtract() throws InterruptedException {
int numberOfThreads = 100; // Increased number of threads
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
List<Callable<Boolean>> tasks = new ArrayList<>();
Option option = new Option(201);
for (int i = 0; i < numberOfThreads; i++) {
tasks.add(() -> option.subtract(50));
}
executorService.invokeAll(tasks);
executorService.shutdown();
assertThat(option.getQuantity()).isLessThan(1);
}
}
在subtract方法中添加打印数量后,就通过了。
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.Test;
class OptionTest {
static class Option {
private int quantity;
public Option(int quantity) {
this.quantity = quantity;
}
public int getQuantity() {
return quantity;
}
public boolean subtract(int quantity){
if(quantity >= this.quantity){
return false;
}
System.out.println("quantity = " + quantity);
this.quantity -= quantity;
return true;
}
}
@Test
void asyncSubtract() throws InterruptedException {
int numberOfThreads = 100; // Increased number of threads
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
List<Callable<Boolean>> tasks = new ArrayList<>();
Option option = new Option(201);
for (int i = 0; i < numberOfThreads; i++) {
tasks.add(() -> option.subtract(50));
}
executorService.invokeAll(tasks);
executorService.shutdown();
assertThat(option.getQuantity()).isLessThan(1);
}
}
测试并不总是应该通过,但有时应该通过,但事实并非如此。当添加 println 时,它确实如此。 谁能解释一下这个测试结果? JVM 有什么魔法吗?
如果您问为什么添加 print 语句会导致程序成功1,那是(可能)因为 print 正在共享输出流数据结构上进行一些底层同步。 这将改变您正在测试的代码的行为,并且可能足以“治愈”您正在尝试测试的内存可见性异常。
虽然未指定写入共享输出流的同步行为(例如,由 javadocs),但您可能希望它发生在典型的 Java 类库中。 (如果在没有技术上正确的同步的情况下使用可能随机中断的输出流类将不被视为“适合目的”。)
1 - 成功是指计算出正确的结果。 我知道您实际上是在尝试证明可能会产生不正确的结果。 从这个角度来说,这是失败的!