为什么copyonwritearaylist或synchronizedlist()上的先读后写操作会导致不一致?

pgky5nke  于 2021-06-29  发布在  Java
关注(0)|答案(0)|浏览(190)

我正在研究并发性,我想了解为什么对以下两种操作之一执行先读后写操作: CopyOnWriteArrayList 或者 synchronizedList() 在多线程场景中导致不确定性行为。
代码如下所示:

public class LoadBalancer {

    private static final Integer MAX_PROVIDERS_SIZE = 10;
    private final List<String> registeredProviders;

    private LoadBalancer(List<String> initialProviders) {
        if (!(initialProviders.size() == new HashSet<>(initialProviders).size())) {
            throw new ProviderAlreadyExists();
        }
        this.registeredProviders = Collections.synchronizedList(initialProviders);
    }

    public static LoadBalancer of(List<String> registeredProviders) {
        return new LoadBalancer(registeredProviders);
    }

    public LoadBalancer registerProvider(String provider) {
        if (registeredProviders.size() >= MAX_PROVIDERS_SIZE) {
            throw new ProvidersLengthExceeded();
        } else {
            registeredProviders.stream().filter(p -> p.equals(provider))
                    .findFirst()
                    .ifPresent(p -> {
                        throw new ProviderAlreadyExists();
                    });
            registeredProviders.add(provider);
            System.out.println("SIZE" + registeredProviders.size());
        }
        return this;
    }

    public List<String> getRegisteredProviders() {
        return registeredProviders;
    }
}

public class ProvidersLengthExceeded extends RuntimeException{

    public ProvidersLengthExceeded() {
        super("The maximum providers size is 10.");
    }
}

public class ProviderAlreadyExists extends RuntimeException{

    public ProviderAlreadyExists() {
        super("The provider already exists.");
    }
}

这就是测试:

@Test
    public void concurrencyCheck() throws InterruptedException {
        List<String> testProviders = IntStream.rangeClosed(0, 8).mapToObj(index -> index + "")
                .collect(Collectors.toList());
        LoadBalancer lb = LoadBalancer.of(testProviders);

        ExecutorService service = Executors.newFixedThreadPool(10);
        IntStream.range(0, 100).forEach(i -> service.submit(() -> {

            lb.registerProvider(UUID.randomUUID().toString());
        }));
        Thread.sleep(10_000);
        System.out.println(lb.getRegisteredProviders().size());
    }
}

我期望的行为是我不能添加超过 MAX_PROVIDERS_SIZE 列表中的项目。通过上面的代码,我得到以下输出:

SIZE11
SIZE10
SIZE13
SIZE12
13

如果在添加之前对过滤部分进行注解,则得到正确的输出:

SIZE10
10

为什么会这样?

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题