问题
When the following piece of code is getting executed I am getting exceptions in a random manner.
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
function:
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();
}
As can be seen from above the code is making use of parallel streams and then calling getResult
Function. Also there is an atomic variable involved. When the atomicVariable is true the store
function is called and when it is false the fetch
function is called.
My understanding is that inside getResult function we are checking and updating atomic variable aBoolean and this check and update operation is atomic but new Store(...).call();
and new Fetch(...).call();
is not and since parallel streams involves multiple threads so there is a race-condition occurring at
return aBoolean.getAndSet(!aBoolean.get()) ?
new Store(new ByteArrayInputStream(loremIpsumContentArray[i / 2]), repository, path).call() :
new Fetch(repository, path).call();
In order to corroborate my theory of Race condition I added the lock
as shown below to both new Store(...).call()
and new Fetch(...).call()
as shown below and then everything worked fine:
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());
And getResult
function:
return aBoolean.getAndSet(!aBoolean.get()) ?
new Store(new ByteArrayInputStream(loremIpsumContentArray[i / 2]), repository, path, lock).call() :
new Fetch(repository, path, lock).call();
I have the following questions:
Is my understanding correct regarding the race condition occurring as mentioned above and have I used the lock in the way they should be?
What are the other ways to avoid the race condition?
Please, let me know your thoughts.
回答1:
Your aBoolean.getAndSet(!aBoolean.get())
is not atomic.
Some thread can hop between !aBoolean.get()
and the surrounding aBoolean.getAndSet
which might lead to the race condition.
You should synchronize the block:
boolean whatToExec;
synchronized (aBoolean) {
whatToExec = aBoolean.get();
aBoolean.set(!whatToExec);
}
return whatToExec ? ...
What the lock is good for in Fetch
or Store
is unknown. If the race condition happens there, I currently have no answer.
来源:https://stackoverflow.com/questions/55843518/race-condition-occurring-during-use-of-parallel-streams-and-atomic-variables