java 线程同步模拟银行_java基础入门-多线程同步浅析-以银行转账为样例

本文通过银行转账的实例,介绍了Java线程同步的概念。在并发情况下,未同步的转账操作导致数据不一致,引入`synchronized`关键字进行同步后,解决了问题。但同步会降低性能,需根据业务需求合理使用。
摘要由CSDN通过智能技术生成

在说之前先普及一下线程是什么?

线程:说白了就是一个任务片段

进程:是一个具有独立功能的程序关于某个数据集合的一次执行活动。一个进程有一个或者多个线程

线程与进程的本质差别就是有么有数据共享空间。线程之间能够共享数据。进程不能够

以下进入主题:线程间的同步

因为如今业务流程添加。业务节点也添加。使用业务的人员也同一时候添加。这个时候就不可避免的出现并发问题,多个线程同一时候訪问操作某一个数据单元

我们以银行转账为例说明,以下先上代码:

建立一个银行的类,里面主要包含三个方法,一个是转账,一个是得到现有银行存款总数,一个是得到如今存户数量

public class Bank {

private final double[] accounts;

public Bank(int n, double initialBalance) {

accounts = new double[n];

for (int i = 0; i < accounts.length; i++) {

accounts[i] = initialBalance;

}

}

public void transfer(int from, int to, double amount) {

if (accounts[from] < amount) {

return;

}

System.out.println(Thread.currentThread());

accounts[from] -= amount;

System.out.printf("%f from %d to %d ", amount, from, to);

accounts[to] += amount;

System.out.println("total:" + getTotalBalance());

}

public double getTotalBalance() {

double sum = 0d;

for (int i = 0; i < accounts.length; i++) {

sum += accounts[i];

}

return sum;

}

public int getAccountSize() {

return accounts.length;

}

}

以下是转账类,由于须要并发操作。所以实现Runnable接口

public class TransferRunnable implements Runnable {

private Bank bank;

private int fromAccount = 0;

private double maxAmount = 0;

public TransferRunnable(Bank b, int fromAccount, double maxAmount) {

this.bank = b;

this.fromAccount = fromAccount;

this.maxAmount = maxAmount;

}

@Override

public void run() {

double amount = maxAmount * Math.random();

int toAccount = (int) ((int) bank.getAccountSize() * Math.random());

bank.transfer(fromAccount, toAccount, amount);

try {

Thread.sleep((long) (100L * Math.random()));

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

以下是測试类:

public class Test {

public static void main(String[] args) {

Bank bank = new Bank(100, 1000);

for (int i = 0; i < 3; i++) {

TransferRunnable transferRunnable = new TransferRunnable(bank, i,

1000);

Thread thread = new Thread(transferRunnable);

thread.start();

}

}

}

输出结果:

Thread[Thread-0,5,main]

Thread[Thread-2,5,main]

Thread[Thread-1,5,main]

430.796266 from 0 to 75

714.274395 from 1 to 88

849.880218 from 2 to 33

total:98435.8453871716

total:99150.11978192833

total:100000.0

我们看上面的结果,特别是最后三行的total总数,发现,第一第二次转账后,总数不正确了。细致观察打印结果,因为并行运行任务,并且中间因为是由cup分配运行顺序。所以我们看到的结果并没有全然依照我们的方法所实现的那样输出出来

因为出现这种问题,我们引入“锁”的概念。因为这里面是浅析,就不针对锁具体说明,以下我们在bank类里面的转账方法上面加上最简单最经常使用的锁synchronized,看看结果是如何:

public class Bank {

private final double[] accounts;

public Bank(int n, double initialBalance) {

accounts = new double[n];

for (int i = 0; i < accounts.length; i++) {

accounts[i] = initialBalance;

}

}

//加了锁

public synchronized void transfer(int from, int to, double amount) {

if (accounts[from] < amount) {

return;

}

System.out.println(Thread.currentThread());

accounts[from] -= amount;

System.out.printf("%f from %d to %d ", amount, from, to);

accounts[to] += amount;

System.out.println("total:" + getTotalBalance());

}

public double getTotalBalance() {

double sum = 0d;

for (int i = 0; i < accounts.length; i++) {

sum += accounts[i];

}

return sum;

}

public int getAccountSize() {

return accounts.length;

}

}

输出结果:

Thread[Thread-0,5,main]

187.754955 from 0 to 50 total:100000.0

Thread[Thread-1,5,main]

282.138799 from 1 to 90 total:100000.0

Thread[Thread-2,5,main]

217.089515 from 2 to 86 total:100000.00000000001

上面的输出结果基本一致,最后一个结果出现小数问题,主要是因为我数据精度的问题,兴许能够通过其它设置来避免。

程序里面因为出现了锁,所以在性能上面不可避免的出现下降,特别是在一些大型程序里面。所以这里面须要依据实际业务所需,把锁的范围锁到比較小的范围,使得性能不会大幅度的下降。

最后我们把上面的东西总结出一个图

49f235125277b3e47be100fe386e98de.png

时间: 07-26

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值