场景练习一:
转账功能
描述:
小新转100给小明,同时小红转50给小新
原有账户余额
代码初版设计
转账功能:
private static void transfer(Account from, Account to, BigDecimal amount) {
if (null == from || null == to || null == amount) {
log.info("【转账】失败.参数不正确");
}
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));
}
}
}
主函数调用
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("小明转账前:" + xiaoming.toString());
System.out.println("小红转账前:" + xiaohong.toString());
System.out.println("小新转账前:" + xiaoxin.toString());
List> results = new ArrayList<>();
results.add(executor.submit(new Callable() {
@Override
public Integer call() throws Exception {
transfer(xiaoxin, xiaoming, BigDecimal.valueOf(100));
return 1;
}
}));
results.add(executor.submit(new Callable() {
@Override
public Integer call() throws Exception {
transfer(xiaohong, xiaoxin, BigDecimal.valueOf(50));
return 1;
}
}));
for (Future result : results) {
try {
result.get();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("------------------------------");
System.out.println("小明转账后:" + xiaoming.toString());
System.out.println("小红转账后:" + xiaohong.toString());
System.out.println("小新转账后:" + xiaoxin.toString());
结果:
小明转账前:Account(id=1, balance=100, name=小明)
小红转账前:Account(id=2, balance=200, name=小红)
小新转账前:Account(id=3, balance=300, name=小新)
------------------------------
小明转账后:Account(id=1, balance=200, name=小明)
小红转账后:Account(id=2, balance=150, name=小红)
小新转账后:Account(id=3, balance=250, name=小新)
看起来好像没啥问题,可是会造成死锁!!!
设想,如果是两个线程,线程A跑小新转给小明100,线程B跑小明转给小新50.这样线程A先获取小新这个账户锁,同时线程B获取小明这个账户锁,双方都拿了对方需要的锁,这样就相处等待⌛️
所以呢,要改进一下,改进方法很多,可以采用lock获取锁失败直接拒绝策略。或者也可以按照一定顺序加锁,这样大家加锁策略就都一致啦~~
优化版:
private static void transferSuccess(Account from, Account to, BigDecimal amount) {
if (null == from || null == to || null == amount) {
log.info("【转账】失败.参数不正确");
}
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));
}
}
}