0

我正在研究并发性,我想了解为什么在以下任一操作上执行读取操作:CopyOnWriteArrayListsynchronizedList()在多线程场景中导致非确定性行为。

代码如下所示:

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

为什么会这样?

4

0 回答 0