BAEL-855 stm (#1855)

* 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
This commit is contained in:
Tomasz Lelek 2017-05-16 16:53:47 +02:00 committed by pedja4
parent ee79ac3a02
commit ce43d80f8b
3 changed files with 213 additions and 15 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent-modules</artifactId>
<groupId>com.baeldung</groupId>
@ -236,7 +236,6 @@
<artifactId>datanucleus-xml</artifactId>
<version>5.0.0-release</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
@ -254,21 +253,25 @@
<version>3.0.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.0.0</version>
</dependency>
<dependency>
<groupId>org.multiverse</groupId>
<artifactId>multiverse-core</artifactId>
<version>${multiverse.version}</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.0.0</version>
</dependency>
</dependencies>
<properties>
<multiverse.version>0.7.0</multiverse.version>
<cglib.version>3.2.4</cglib.version>
<commons-lang.version>3.5</commons-lang.version>
<jasypt.version>1.9.2</jasypt.version>

View File

@ -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));
}
}

View File

@ -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);
}
}