ABA problem in concurrency

This commit is contained in:
Gergo Petrik 2020-05-04 16:40:20 +02:00
parent a04fb286e4
commit a6fc46e3f6
2 changed files with 127 additions and 0 deletions

View File

@ -0,0 +1,34 @@
package com.baeldung.abaproblem;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Account {
private AtomicInteger balance = new AtomicInteger(0);
public int getBalance() {
return balance.get();
}
public boolean withdraw(int amount) throws InterruptedException {
int current = getBalance();
if (current < amount) {
throw new RuntimeException("Not sufficient balance");
}
precessBalance();
return balance.compareAndSet(current, current - amount);
}
private void precessBalance() throws InterruptedException {
if ("thread 1".equals(Thread.currentThread().getName())) {
TimeUnit.SECONDS.sleep(2);
}
}
public boolean deposit(int amount) {
int current = balance.get();
return balance.compareAndSet(current, current + amount);
}
}

View File

@ -0,0 +1,93 @@
package com.baeldung.abaproblem;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class AccountUnitTest {
private Account account;
@BeforeEach
public void setUp() {
account = new Account();
}
@Test
public void zeroBalanceInitializationTest() {
assertEquals(0, account.getBalance());
}
@Test
public void depositTest() {
final int moneyToDeposit = 50;
assertTrue(account.deposit(moneyToDeposit));
assertEquals(moneyToDeposit, account.getBalance());
}
@Test
public void withdrawTest() throws InterruptedException {
final int defaultBalance = 50;
final int moneyToWithdraw = 20;
account.deposit(defaultBalance);
assertTrue(account.withdraw(moneyToWithdraw));
assertEquals(defaultBalance - moneyToWithdraw, account.getBalance());
}
@Test
public void withdrawWithoutSufficientBalanceTest() {
assertThrows(RuntimeException.class, () -> account.withdraw(10));
}
@Test
public void abaProblemTest() throws InterruptedException {
final int defaultBalance = 50;
final int amountToWithdrawByThreadA = 20;
final int amountToWithdrawByThreadB = 10;
final int amountToDepositByThreadB = 10;
account.deposit(defaultBalance);
Thread threadA = new Thread(() -> {
try {
// this will take longer due to the name of the thread
assertTrue(account.withdraw(amountToWithdrawByThreadA));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "tread 1");
Thread threadB = new Thread(() -> {
assertTrue(account.deposit(amountToDepositByThreadB));
assertEquals(defaultBalance + amountToDepositByThreadB, account.getBalance());
try {
// this will be fast due to the name of the thread
assertTrue(account.withdraw(amountToWithdrawByThreadB));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// thread 1 didn't finish yet, so the original value will be in place for it
assertEquals(defaultBalance, account.getBalance());
}, "thread 2");
threadA.start();
threadB.start();
threadA.join();
threadB.join();
// compareAndSet operation succeeds for thread 1
assertEquals(defaultBalance - amountToWithdrawByThreadA, account.getBalance());
}
}