/** 两个账户总和大于等于1000才能取款
In Handling Write Skew Anomaly, we discussed write skew and how Clojure STM handles it. Akka also has support for dealing with write skew, *but we have to configure it*. OK, that word may sound scary, but it’s really simple. Let’s first see the default behavior without any configuration. Let’s revisit the example of multiple accounts with a restricted combined balance that we looked at earlier. Create a Portfolio class that holds a checking account balance and a savings account balance. These two accounts have a constraint of the total balance not running less than $1,000. This class along with the withdraw() method is shown next. In this method, we obtain the two balances first, compute their total, and after a intentional delay (introduced to set transactions on collision course) we subtract the given amount from either the checking balance or the savings balance if the total is not less than $1,000. The withdraw() method does its operations within a transaction configured using default settings.
*/
public class Portfolio {
final private Ref<Integer> checkingBalance =
new Ref<Integer>(
500);
final private Ref<Integer> savingsBalance =
new Ref<Integer>(
600);
public int getCheckingBalance() {
return checkingBalance.get(); }
public int getSavingsBalance() {
return savingsBalance.get(); }
public void withdraw(
final boolean fromChecking,
final int amount) {
new Atomic<Object>() {
public Object
atomically() {
final int totalBalance =checkingBalance.get() + savingsBalance.get();
try { Thread.sleep(
1000); }
catch(InterruptedException ex) {}
if(totalBalance - amount >=
1000) {
if(fromChecking)
checkingBalance.swap(checkingBalance.get() - amount);
else
savingsBalance.swap(savingsBalance.get() - amount);
}
else
System.out.println(
"Sorry, can't withdraw due to constraint violation");
return null;
}
}.execute();
}
}
public class UsePortfolio {
public static void main(
final String[] args)
throws InterruptedException {
final Portfolio portfolio =
new Portfolio();
int checkingBalance = portfolio.getCheckingBalance();
int savingBalance = portfolio.getSavingsBalance();
System.out.println(
"Checking balance is " + checkingBalance);
System.out.println(
"Savings balance is " + savingBalance);
System.out.println(
"Total balance is " +
(checkingBalance + savingBalance));
final ExecutorService service = Executors.newFixedThreadPool(
10);
service.execute(
new Runnable() {
public void run() { portfolio.withdraw(
true,
100); }
});
service.execute(
new Runnable() {
public void run() { portfolio.withdraw(
false,
100); }
});
service.shutdown();
Thread.sleep(
4000);
checkingBalance = portfolio.getCheckingBalance();
savingBalance = portfolio.getSavingsBalance();
System.out.println(
"Checking balance is " + checkingBalance);
System.out.println(
"Savings balance is " + savingBalance);
System.out.println(
"Total balance is " +
(checkingBalance + savingBalance));
if(checkingBalance + savingBalance <
1000)
System.out.println(
"Oops, broke the constraint!");
}
}
解析: By default, Akka does not avoid write skew, and the two transactions will proceed running the balances into constraint violation, as we see in the output: 结果1 :
Checking balance
is 500
Savings balance
is 600
Total balance
is 1100
Checking balance
is 400
Savings balance
is 500
Total balance
is 900
Oops, broke
the constraint!
FIX:
new Atomic<Object>() {
to the following:
akka.stm.TransactionFactory factory =
new akka.stm.TransactionFactoryBuilder()
.setWriteSkew(false)
.setTrackReads(true)
.build();
new Atomic<Object>(factory) {
...
We created a TransactionFactoryBuilder and set the writeSkew and trackReads properties to false and true, respectively. This tells the transaction to keep track of reads within a transaction and also to set a read lock on the reads until the commit begins, just as Clojure STM handles ensure.
转载请注明原文地址: https://ju.6miu.com/read-8205.html