使用并行流和原子变量期间发生的竞争状况

时间:2019-04-25 06:58:37

标签: java multithreading java-stream atomic strongbox

下面的代码执行时,我会以随机方式获取异常。

byte[][] loremIpsumContentArray = new byte[64][];

for (int i = 0; i < loremIpsumContentArray.length; i++)
{
    random.nextBytes(loremIpsumContentArray[i] = new byte[CONTENT_SIZE]);
}

AtomicBoolean aBoolean = new AtomicBoolean(true);
List<Long> resultList = IntStream.range(0, 64* 2)
                                 .parallel()
                                 .mapToObj(i -> getResult(i,
                                                          aBoolean,
                                                          repositoryPath,
                                                          loremIpsumContentArray ))
                                 .collect(Collectors.toList());

getResult功能:

try
{
    Repository repository = repositoryPath.getRepository();
    String path = RepositoryFiles.relativizePath(repositoryPath);

    //return aBoolean.compareAndSet(aBoolean.get(), !aBoolean.get()) ?
    return aBoolean.getAndSet(!aBoolean.get()) ?
                              new Store(new ByteArrayInputStream(loremIpsumContentArray[i / 2]), repository, path, lock).call() :
                              new Fetch(repository, path, lock).call();
}

从上面可以看出,代码正在使用并行流,然后调用getResult函数。此外,还涉及一个原子变量。当atomicVariable为true时,将调用store函数;为false时,将调用fetch函数。

我的理解是,在getResult函数内部,我们正在检查和更新原子变量aBoolean,并且此检查和更新操作是原子的,但是new Store(...).call();new Fetch(...).call();不是原子的,并且由于并行流涉及多个线程,因此存在发生在

的比赛条件
return aBoolean.getAndSet(!aBoolean.get()) ?
                          new Store(new ByteArrayInputStream(loremIpsumContentArray[i / 2]), repository, path).call() :
                          new Fetch(repository, path).call();

为了证实我的种族条件理论,我将如下所示的lock分别添加到了new Store(...).call()new Fetch(...).call()中,如下所示,然后一切正常很好:

Lock lock = new ReentrantLock();
AtomicBoolean aBoolean = new AtomicBoolean(true);
List<Long> resultList = IntStream.range(0, 64* 2)
                                 .parallel()
                                 .mapToObj(i -> getResult(i,
                                                          aBoolean,
                                                          repositoryPath,
                                                          loremIpsumContentArray,
                                                          lock))
                                 .collect(Collectors.toList());

getResult函数:

return aBoolean.getAndSet(!aBoolean.get()) ?
                          new Store(new ByteArrayInputStream(loremIpsumContentArray[i / 2]), repository, path, lock).call() :
                          new Fetch(repository, path, lock).call();

我有以下问题:

  • 对于上述发生的比赛情况,我的理解是否正确,我是否按应有的方式使用了锁?

  • 还有其他避免比赛状况的方法吗?

请让我知道你的想法。

1 个答案:

答案 0 :(得分:1)

您的aBoolean.getAndSet(!aBoolean.get())不是原子的。 某些线程可能会在!aBoolean.get()与周围的aBoolean.getAndSet之间跳来跳去,这可能会导致竞争状态。

您应该同步块:

boolean whatToExec;
synchronized (aBoolean) {
    whatToExec = aBoolean.get();
    aBoolean.set(!whatToExec);
}
return whatToExec ? ...

FetchStore中,锁定的作用是未知的。如果比赛条件在那里发生,我目前没有答案。