forked from eugenp/tutorials
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* BAEL-855 code for the STM article * BAEL-855 method ordering * BAEL-855 Better test case * BAEL-855 formatting * BAEL-855 rename * BAEL-855 change to expected * Merge branch 'master' of https://github.com/eugenp/tutorials into BAEL-855_stm # Conflicts: # libraries/pom.xml
- Loading branch information
Showing
3 changed files
with
213 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.baeldung.stm; | ||
|
||
import org.multiverse.api.StmUtils; | ||
import org.multiverse.api.callables.TxnCallable; | ||
import org.multiverse.api.references.TxnInteger; | ||
import org.multiverse.api.references.TxnLong; | ||
|
||
public class Account { | ||
|
||
private final TxnLong lastUpdate; | ||
private final TxnInteger balance; | ||
|
||
public Account(final int balance) { | ||
this.lastUpdate = StmUtils.newTxnLong(System.currentTimeMillis()); | ||
this.balance = StmUtils.newTxnInteger(balance); | ||
} | ||
|
||
public Integer getBalance() { | ||
return balance.atomicGet(); | ||
} | ||
|
||
public void adjustBy(final int amount) { | ||
adjustBy(amount, System.currentTimeMillis()); | ||
} | ||
|
||
public void adjustBy(final int amount, final long date) { | ||
StmUtils.atomic(() -> { | ||
balance.increment(amount); | ||
lastUpdate.set(date); | ||
|
||
if (balance.get() < 0) { | ||
throw new IllegalArgumentException("Not enough money"); | ||
} | ||
}); | ||
} | ||
|
||
public void transferTo(final Account other, final int amount) { | ||
StmUtils.atomic(() -> { | ||
final long date = System.currentTimeMillis(); | ||
adjustBy(-amount, date); | ||
other.adjustBy(amount, date); | ||
}); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return StmUtils.atomic((TxnCallable<String>) | ||
txn -> "Balance: " + balance.get(txn) + " lastUpdateDate: " + lastUpdate.get(txn)); | ||
} | ||
} |
145 changes: 145 additions & 0 deletions
145
libraries/src/test/java/com/baeldung/stm/AccountTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package com.baeldung.stm; | ||
|
||
|
||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.ExpectedException; | ||
|
||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
import static org.assertj.core.api.Java6Assertions.assertThat; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
public class AccountTest { | ||
|
||
@Test | ||
public void givenAccount_whenDecrement_thenShouldReturnProperValue() { | ||
//given | ||
Account a = new Account(10); | ||
|
||
//when | ||
a.adjustBy(-5); | ||
|
||
//then | ||
assertThat(a.getBalance()).isEqualTo(5); | ||
} | ||
|
||
@Test(expected = IllegalArgumentException.class) | ||
public void givenAccount_whenDecrementTooMuch_thenShouldThrow() { | ||
//given | ||
Account a = new Account(10); | ||
|
||
//when | ||
a.adjustBy(-11); | ||
} | ||
|
||
@Test | ||
public void givenTwoThreads_whenBothApplyOperation_thenShouldThrow() throws InterruptedException { | ||
//given | ||
ExecutorService ex = Executors.newFixedThreadPool(2); | ||
Account a = new Account(10); | ||
CountDownLatch countDownLatch = new CountDownLatch(1); | ||
AtomicBoolean exceptionThrown = new AtomicBoolean(false); | ||
|
||
//when | ||
ex.submit(() -> { | ||
try { | ||
countDownLatch.await(); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
try { | ||
a.adjustBy(-6); | ||
} catch (IllegalArgumentException e) { | ||
exceptionThrown.set(true); | ||
} | ||
}); | ||
ex.submit(() -> { | ||
try { | ||
countDownLatch.await(); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
try { | ||
a.adjustBy(-5); | ||
} catch (IllegalArgumentException e) { | ||
exceptionThrown.set(true); | ||
} | ||
}); | ||
|
||
countDownLatch.countDown(); | ||
ex.awaitTermination(1, TimeUnit.SECONDS); | ||
ex.shutdown(); | ||
|
||
//then | ||
assertTrue(exceptionThrown.get()); | ||
} | ||
|
||
@Test | ||
public void givenTwoAccounts_whenFailedWhileTransferring_thenShouldRollbackTransaction() { | ||
//given | ||
final Account a = new Account(10); | ||
final Account b = new Account(10); | ||
|
||
//when | ||
a.transferTo(b, 5); | ||
|
||
//then | ||
assertThat(a.getBalance()).isEqualTo(5); | ||
assertThat(b.getBalance()).isEqualTo(15); | ||
|
||
//and | ||
try { | ||
a.transferTo(b, 20); | ||
} catch (final IllegalArgumentException e) { | ||
System.out.println("failed to transfer money"); | ||
} | ||
|
||
//then | ||
assertThat(a.getBalance()).isEqualTo(5); | ||
assertThat(b.getBalance()).isEqualTo(15); | ||
} | ||
|
||
@Test | ||
public void givenTwoThreads_whenBothTryToTransfer_thenShouldNotDeadlock() throws InterruptedException { | ||
//given | ||
ExecutorService ex = Executors.newFixedThreadPool(2); | ||
final Account a = new Account(10); | ||
final Account b = new Account(10); | ||
CountDownLatch countDownLatch = new CountDownLatch(1); | ||
|
||
//when | ||
ex.submit(() -> { | ||
try { | ||
countDownLatch.await(); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
a.transferTo(b, 10); | ||
}); | ||
ex.submit(() -> { | ||
try { | ||
countDownLatch.await(); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
b.transferTo(a, 1); | ||
|
||
}); | ||
|
||
countDownLatch.countDown(); | ||
ex.awaitTermination(1, TimeUnit.SECONDS); | ||
ex.shutdown(); | ||
|
||
//then | ||
assertThat(a.getBalance()).isEqualTo(1); | ||
assertThat(b.getBalance()).isEqualTo(19); | ||
} | ||
|
||
|
||
} |