場景練習(xí)一:
轉(zhuǎn)賬功能
描述:
小新轉(zhuǎn)100給小明,同時小紅轉(zhuǎn)50給小新
原有賬戶余額

image.png
代碼初版設(shè)計(jì)
轉(zhuǎn)賬功能:
private static void transfer(Account from, Account to, BigDecimal amount) {
if (null == from || null == to || null == amount) {
log.info("【轉(zhuǎn)賬】失敗.參數(shù)不正確");
}
synchronized (from) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
log.error("異常", e);
}
synchronized (to) {
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
}
}
}
主函數(shù)調(diào)用
Account xiaoming = new Account("1", BigDecimal.valueOf(100), "小明");
Account xiaohong = new Account("2", BigDecimal.valueOf(200), "小紅");
Account xiaoxin = new Account("3", BigDecimal.valueOf(300), "小新");
System.out.println("小明轉(zhuǎn)賬前:" + xiaoming.toString());
System.out.println("小紅轉(zhuǎn)賬前:" + xiaohong.toString());
System.out.println("小新轉(zhuǎn)賬前:" + xiaoxin.toString());
List<Future<Integer>> results = new ArrayList<>();
results.add(executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
transfer(xiaoxin, xiaoming, BigDecimal.valueOf(100));
return 1;
}
}));
results.add(executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
transfer(xiaohong, xiaoxin, BigDecimal.valueOf(50));
return 1;
}
}));
for (Future<Integer> result : results) {
try {
result.get();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("------------------------------");
System.out.println("小明轉(zhuǎn)賬后:" + xiaoming.toString());
System.out.println("小紅轉(zhuǎn)賬后:" + xiaohong.toString());
System.out.println("小新轉(zhuǎn)賬后:" + xiaoxin.toString());
結(jié)果:
小明轉(zhuǎn)賬前:Account(id=1, balance=100, name=小明)
小紅轉(zhuǎn)賬前:Account(id=2, balance=200, name=小紅)
小新轉(zhuǎn)賬前:Account(id=3, balance=300, name=小新)
------------------------------
小明轉(zhuǎn)賬后:Account(id=1, balance=200, name=小明)
小紅轉(zhuǎn)賬后:Account(id=2, balance=150, name=小紅)
小新轉(zhuǎn)賬后:Account(id=3, balance=250, name=小新)
看起來好像沒啥問題,可是會造成死鎖?。?!
設(shè)想,如果是兩個線程,線程A跑小新轉(zhuǎn)給小明100,線程B跑小明轉(zhuǎn)給小新50.這樣線程A先獲取小新這個賬戶鎖,同時線程B獲取小明這個賬戶鎖,雙方都拿了對方需要的鎖,這樣就相處等待??
所以呢,要改進(jìn)一下,改進(jìn)方法很多,可以采用lock獲取鎖失敗直接拒絕策略?;蛘咭部梢园凑找欢樞蚣渔i,這樣大家加鎖策略就都一致啦~~
優(yōu)化版:
private static void transferSuccess(Account from, Account to, BigDecimal amount) {
if (null == from || null == to || null == amount) {
log.info("【轉(zhuǎn)賬】失敗.參數(shù)不正確");
}
Account min = from.getId().compareTo(to.getId()) > 0 ? to : from;
Account max = from.getId().compareTo(to.getId()) > 0 ? from : to;
synchronized (min) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (max) {
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
}
}
}
??