以银行取钱为例模拟Java多线程同步问题完整代码

 更新时间:2017年12月19日 10:36:22   作者:李学凯  
这篇文章主要介绍了以银行取钱为例模拟Java多线程同步问题完整代码,具有一定借鉴价值,需要的朋友可以参考下。

简单了解下在操作系统中进程和线程的区别:

  进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)

  线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)

  线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。

  多进程是指操作系统能同时运行多个任务(程序)。

  多线程是指在同一程序中有多个顺序流在执行。首先存钱取钱的这个操作,应该是线程操作的,可以有很多的顾客,这意思就是得有多个线程,多个线程之间共同操作一个银行,银行的金额就需要同步。才能保证线程安全。

所以,下面就把这个代码的实例放这,有不对的地方,还请指出来哈。因为有个老铁问这个多线程的代码。

首先是银行,这个对象model的创建。

package com.lxk.threadTest.bank;
/** 
 * 银行model,一个总金额属性。 
 * <p> 
 * 
 * @author lxk on 2017/6/26 
 */
public class Bank {
	/** 
   * 给银行个启动资金,不然怎么干生意呢。 
   */
	private int sum = 200;
	//这个从来不这么用,但也算是正确的一种加锁的机制:同步代码块。 
	//Object obj = new Object(); 
	/** 
   * 存钱 
   * 要是不加[synchronized--同步函数],则会出现多线程安全问题。 
   */
	public synchronized void add(int n) {
		//synchronized (obj) { 
		sum = sum + n;
		try {
			Thread.sleep(10);
		}
		catch (Exception ignore) {
		}
		//当存钱次数变多的时候,就可以发现,存钱的线程确实是2个在交替执行存钱这个动作的。 
		System.out.println(Thread.currentThread().getName() + "...sum=" + sum);
		//}
	}
	/** 
   * 取钱 
   * 要是不加[synchronized--同步函数],则会出现多线程安全问题。 
   */
	public synchronized void reduce(int n) {
		if (sum - n >= 0) {
			sum = sum - n;
		} else {
			System.out.println("bank's money is not enough !");
		}
		try {
			Thread.sleep(30);
		}
		catch (Exception ignore) {
		}
		//当存钱次数变多的时候,就可以发现,存钱的线程确实是2个在交替执行存钱这个动作的。 
		System.out.println(Thread.currentThread().getName() + "...sum=" + sum);
	}
}

在代码里面有存和取2个方法,这2个方法,以及一个总金额,里面有部分被注释掉的代码,那个是简单易懂好理解的,多线程加锁互斥,保证线程间同步的方法。

但是这个是不常用的方法,常用的就是使用synchronized这个关键字来修饰同步方法。

客户对象的model

package com.lxk.threadTest.bank;
/** 
 * 顾客,实现runnable()接口,多个人可以一起存钱 
 * 
 * @author lxk on 2017/6/26 
 */
public class Customer implements Runnable {
	/** 
   * 存钱类型 
   */
	static final String TYPE_ADD = "add";
	/** 
   * 取钱类型 
   */
	static final String TYPE_REDUCE = "reduce";
	/** 
   * 银行 
   */
	private Bank bank;
	/** 
   * 对钱的操作类型,存钱or取钱 
   */
	private String type;
	/** 
   * 操作的次数,理论上是个正数 
   */
	private int time;
	/** 
   * 要存或者取多少钱 
   */
	private int money;
	public Customer() {
	}
	public Customer(Bank bank, String type, int time, int money) {
		this.bank = bank;
		this.type = type;
		this.time = time;
		this.money = money;
	}
	@Override 
	  public void run() {
		for (int x = 0; x < time; x++) {
			if (TYPE_ADD.equals(type)) {
				bank.add(money);
			} else if (TYPE_REDUCE.equals(type)) {
				bank.reduce(money);
			}
		}
	}
}

客户对象,因为可以很多个客户同时访问一个银行,所以,这个存钱取钱的操作就用线程来实现。

属性就构造方法传值了。

main方法

package com.lxk.threadTest.bank;
/** 
 * 银行存钱的多线程实例 
 * <p> 
 * 【需求:】 
 * 银行有一个金库。 
 * 有两个储户分别存或者取n * 100。 
 * 目的:该程序是否有安全问题,如果有,如何解决? 
 * <p> 
 * 【如何找问题:】 
 * 1,明确哪些代码是多线程运行代码。 
 * 2,明确共享数据。 
 * 3,明确多线程运行代码中哪些语句是操作共享数据的。 
 * 
 * @author lxk on 2017/6/26 
 */
public class Main {
	public static void main(String[] args) {
		//一个银行and多个客户 
		Bank bank = new Bank();
		int time = 10000;
		int money = 100;
		//这个客户存钱 
		Customer c1 = new Customer(bank, Customer.TYPE_ADD, time, money);
		//这个客户取钱 
		Customer c2 = new Customer(bank, Customer.TYPE_REDUCE, time, money);
		Thread t1 = new Thread(c1);
		Thread t2 = new Thread(c2);
		t1.start();
		t2.start();
	}
}

上述代码实际运行效果如下图。

这个存取钱的次数要是小了,就可能会看到2个线程有先后顺序,所以,这个次数咱整多点,然后,就看到如图所示的情况,线程1是取钱的,线程0时存钱的,可以看到2个线程是互相交错执行的,有存有取,没有规律可言。

这个就保证了数据的同步了。

至于如何才能不同步,也就是异常的现象,

你可以把add方法的这个synchronized关键字去掉之后,把次数调小一点改成3次,sum的初始值给设置成0.你再试试代码,

就会发现所谓的不同步现象。

上图的右边就是不同步的结果,2个人每次存100,存三次,总数是不是得,100,200,300,400,500,600.得长。

但是,运行结果却不是的,

这个时候,你再把synchronized给add方法加上去,就会出现左边的图的结果,这个就是正确的结果。

我是为了,有存有取,所以,就又加了个方法。代码就变成上面的样子啦。

差不多都是线程间同步的例子啦。

我就简单记录下代码。用的时候,可以分分钟就拿出来。

总结

以上就是本文关于以银行取钱为例模拟Java多线程同步问题完整代码的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

java多线程编程实例

Java多线程定时器Timer原理及实现

Java通过卖票理解多线程

如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

相关文章

  • Java生成二维码的实例代码

    Java生成二维码的实例代码

    这篇文章主要介绍了Java生成二维码的实例代码,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-09-09
  • 深入理解Java设计模式之建造者模式

    深入理解Java设计模式之建造者模式

    这篇文章主要介绍了JAVA设计模式之建造者模式的的相关资料,文中示例代码非常详细,供X大家参考和学习,感兴趣的朋友可以了解下
    2021-11-11
  • Springboot项目通过redis实现接口的幂等性

    Springboot项目通过redis实现接口的幂等性

    这篇文章主要为大家介绍了Springboot项目通过redis实现接口的幂等性,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • kafka运维consumer-groups.sh消费者组管理

    kafka运维consumer-groups.sh消费者组管理

    这篇文章主要为大家介绍了kafka运维consumer-groups.sh消费者组管理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Java详细讲解包的作用以及修饰符的介绍

    Java详细讲解包的作用以及修饰符的介绍

    本文主要讲述的是包的使用和注意事项和四种访问修饰符public,protected,默认的,private的访问范围及实例,感兴趣的朋友一起来看看
    2022-05-05
  • java实现多图片上传功能

    java实现多图片上传功能

    这篇文章主要为大家详细介绍了java多图片同步上传功能的实现方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • PageHelper插件实现一对多查询时的分页问题

    PageHelper插件实现一对多查询时的分页问题

    这篇文章主要介绍了PageHelper插件实现一对多查询时的分页问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • spring cloud如何修复zuul跨域配置异常的问题

    spring cloud如何修复zuul跨域配置异常的问题

    最近的开发过程中,使用spring集成了spring-cloud-zuul,在配置zuul跨域的时候遇到了问题,下面这篇文章主要给大家介绍了关于spring cloud如何修复zuul跨域配置异常的问题,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-09-09
  • Java9新特性Stream流API优化与增强

    Java9新特性Stream流API优化与增强

    这篇文章主要为大家介绍了Java9新特性Stream流API优化与增强的用法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助祝大家多多进步,早日升职加薪
    2022-03-03
  • Java使用pdfbox实现给pdf文件加图片水印

    Java使用pdfbox实现给pdf文件加图片水印

    有时候需要给pdf加水印,市面上工具都是收费的要会员,还是自食其力吧;尝试过 spire.pdf.free 那个超过10页就不行了!所以本文还是使用了pdfbox,感兴趣的可以了解一下
    2022-11-11

最新评论