zoukankan      html  css  js  c++  java
  • java基础入门-多线程同步浅析-以银行转账为例子

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

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

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


    线程与进程的本质区别就是有么有数据共享空间,线程之间可以共享数据,进程不可以


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

    由于现在业务流程增加,业务节点也增加,使用业务的人员也同时增加,这个时候就不可避免的出现并发问题,多个线程同时访问操作某一个数据单元

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

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

    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


    上面的输出结果基本一致,最后一个结果出现小数问题,主要是由于我数据精度的问题,后续可以通过其他设置来避免。


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


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





    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    地铁项目结对编程
    地铁项目初步计划及简单设计
    构建之法浅读感想
    集美大学1511,1512软件工程课程作业总结
    第二次作业小结
    第二次作业评分可能要 delay 一些
    第一次作业小结
    关于我
    面向对象设计与构造第四单元总结及期终总结
    面向对象设计与构造第三单元作业总结
  • 原文地址:https://www.cnblogs.com/raylee2007/p/4774567.html
Copyright © 2011-2022 走看看