package thread.test;
import java.math.BigDecimal;
import java.util.Currency;
public abstract class Amount implements Comparable{
public abstract BigDecimal getBalance();
public abstract void setBalance(BigDecimal balance);
public abstract Currency getCurrency();
@Override
public int compareTo(Amount o) {
if(o==null){
throw new NullPointerException("null arg.");
}
if(this.getBalance()==null||o.getBalance()==null){
throw new NullPointerException("null arg.");
}
return this.getBalance().compareTo(o.getBalance());
}
}
package thread.test;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Locale;
public class DollarAmount extends Amount{
private BigDecimal balance;
private Currency currency = Currency.getInstance(Locale.US);
public DollarAmount(BigDecimal balance){
this.balance = balance;
}
public BigDecimal getBalance() {
return balance;
}
public void setAmount(BigDecimal amount) {
this.balance = amount;
}
public Currency getCurrency() {
return currency;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}
package thread.test;
import java.math.BigDecimal;
public class Account {
private String id;
private Amount balance;
public Account(Amount amount,String id){
this.balance = amount;
this.id = id;
}
public Amount getBalance() {
return balance;
}
public void setBalance(Amount balance) {
this.balance = balance;
}
public void debit(Amount amount){
if(this.balance==null||amount==null||amount.getBalance()==null){
return;
}
System.out.println(id+" 支出金额"+amount.getBalance());
//修正账户余额:本账户减去借方金额
BigDecimal current = this.balance.getBalance();
BigDecimal now = current.subtract(amount.getBalance());
this.balance.setBalance(now);
}
public void credit(Amount amount){
if(this.balance==null||amount==null||amount.getBalance()==null){
return;
}
System.out.println(id+" 收入金额"+amount.getBalance());
//修正账户余额:本账户加贷方金额
BigDecimal current = this.balance.getBalance();
BigDecimal now = current.add(amount.getBalance());
this.balance.setBalance(now);
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
package thread.test;
public class AccountHelper {
//加时赛锁
private final Object tieLock = new Object();
public void transferMoney(final Account fromAcct,
final Account toAcct,
final Amount amount){
//参数校验
if(fromAcct==null||toAcct==null||amount==null){
throw new IllegalArgumentException("null arg.");
}
//余额校验
if(fromAcct.getBalance().compareTo(amount)<0){
throw new IllegalArgumentException(fromAcct.getId()+"账户余额不足");
}
//根据对象的hash值随机设置加锁顺序
int fromHash = System.identityHashCode(fromAcct);
int toHash = System.identityHashCode(toAcct);
if(fromHashtoHash){
synchronized (toAcct) {
synchronized (fromAcct) {
this.transfer(fromAcct, toAcct, amount);
}
}
}else{
//碰巧相对时,先获取加时赛锁
synchronized (tieLock) {
synchronized (fromAcct) {
synchronized (toAcct) {
this.transfer(fromAcct, toAcct, amount);
}
}
}
}
}
//transfer对两个账户的操作必须是原子的完成
private void transfer(final Account fromAcct,
final Account toAcct,
final Amount amount){
System.out.println("Thread "+Thread.currentThread().getName()+" do transfer.");
fromAcct.debit(amount);
toAcct.credit(amount);
}
}
package thread.test;
import java.math.BigDecimal;
public class MainTest {
public static void main(String[] args) {
Amount amFromAcc = new DollarAmount(new BigDecimal(2000));
Amount amToAcc = new DollarAmount(new BigDecimal(1000));
final AccountHelper h = new AccountHelper();
final Account fromAcc = new Account(amFromAcc,"zhang_3");
final Account toAcc = new Account(amToAcc,"wang_5");
final Amount amToTran = new DollarAmount(new BigDecimal(1));
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
h.transferMoney(fromAcc, toAcc, amToTran);
}
});
Thread t4 = new Thread(new Runnable(){
@Override
public void run() {
h.transferMoney(fromAcc, toAcc, amToTran);
}
});
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
h.transferMoney(toAcc, fromAcc, amToTran);
}
});
Thread t3 = new Thread(new Runnable(){
@Override
public void run() {
h.transferMoney(toAcc, fromAcc, amToTran);
}
});
t1.start();
t2.start();
t3.start();
t4.start();
}
}