今天了解多线程的过程中,随手写了一个银行存钱的Demo,对于部分业务需要用到同步机制。
先看代码:
这是模拟银行账户的抽象类:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Author:KrisYu * @Description:银行账户 */ public class Account { //账户余额 private Double balance = 0d; //存钱 public void saveMoney(Double money) { Double newMoney = balance + money; //模拟存钱需要时间 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } balance = newMoney; } //查看余额 public Double getBalence() { System.out.println("还剩余额:" + balance + "元"); return balance; } }
这是把存钱的操作单独抽成一个类,用于线程操作
/** * @Author:KrisYu * @Description:存钱线程 */ public class AddMoneyThread implements Runnable { private Account account; private Double money; public AddMoneyThread(Account account,Double money){ this.account = account; this.money = money; } public void run() { account.saveMoney(money); } }
最后是操作类,简历线程池,用于实现100次的线程操作
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @Author:KrisYu * @Description:存钱线程 */ public class AccountTest { public static void main(String[] args) { Account yuAccount = new Account(); Double money = 1d; AddMoneyThread addMoneyThread = new AddMoneyThread(yuAccount, money); ExecutorService executorService = Executors.newFixedThreadPool(100); for (int i = 0; i < 100; i++) { executorService.execute(new AddMoneyThread(yuAccount, money)); } executorService.shutdown(); //确保线程关闭完全,不然的话是死循环 while (!executorService.isTerminated()) { } yuAccount.getBalence(); } }
执行100词存钱,每次存一元,但是惊奇的发现 打印的结果往往只有个位数甚至1元,让我们进入思考,多个线程并发运行,大多数线程都是取到了0余额然后对其加1元所以结果不在预料之内。要改正只能保证每一次存钱操作同步进行,我在这里提供三个思路
一:
直接在存钱操作上加上synchronized关键字保证saveMoney()方法上锁,同一时刻最多有一个线程来执行这个方法。
//存钱 public synchronized void saveMoney(Double money) { Double newMoney = balance + money; //模拟存钱需要时间 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } balance = newMoney; }
二:直接再重写的run方法给Account对象上锁,保证对这个对象最多有一个线程来进入对象修改信息
public void run() { synchronized (account) { account.saveMoney(money); } }
三:在saveMoney()方法中上重入锁把存钱相关代码上锁,并在执行完成后解锁
//重入锁 private Lock accountLock = new ReentrantLock(); //存钱 public synchronized void saveMoney(Double money) { accountLock.lock(); try { Double newMoney = balance + money; //模拟存钱需要时间 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } balance = newMoney; } finally { accountLock.unlock(); } }
好,今天写到这里,欢迎大家在评论区回答你最喜欢的同步方式,并写上原因。
参考文献:骆昊的博客