为什么在 CopyOnWriteArrayList 或 synchronizedList() 上的写操作之前读会导致不一致?

问题描述

我正在研究并发性,我想了解为什么在多线程场景中先读后写操作: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

为什么会这样?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...