java编程多线程并发处理实例解析
作者:sherry_Rui 发布时间:2022-06-02 22:14:59
本文主要是通过一个银行用户取钱的实例,演示java编程多线程并发处理场景,具体如下。
从一个例子入手:实现一个银行账户取钱场景的实例代码。
第一个类:Account.java
账户类:
package cn.edu.byr.test;
public class Account {
private String accountNo;
private double balance;
public Account(){
}
public Account(String accountNo,double balance){
this.accountNo = accountNo;
this.balance = balance;
}
public int hashcode(){
return accountNo.hashCode();
}
public String getAccountNo(){
return this.accountNo;
}
public double getBalance(){
return this.balance;
}
public void setBalance(double balance){
this.balance = balance;
}
public Boolean equals(Object obj){
if(this == obj)
return true;
if(obj != null && obj.getClass() == Account.class){
Account target = (Account)obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
}
第二个类:DrawThread.java
取钱线程类:
package cn.edu.byr.test;
public class DrawThread extends Thread {
private Account account;
private double drawAmount;
public DrawThread(String name,Account account,double drawAmount){
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
public void run(){
// synchronized (account) {
if(account.getBalance() > drawAmount){
System.out.println(getName() + "取钱成功,吐出钞票:" + drawAmount);
// try{
// Thread.sleep(1);
// }
// catch(InterruptedException e){
// e.printStackTrace();
// }
account.setBalance(account.getBalance() - drawAmount);
System.out.println("\t 余额为 : " + account.getBalance());
} else
System.out.println(getName() + "取钱失败,余额不足!");
// }
}
public static void main(String[] args){
Account acct = new Account("123456",1000);
new DrawThread("A",acct,800).start();
new DrawThread("B",acct,800).start();
}
}
上面代码中注释掉的部分:(1)synchronized同步代码块 (2)线程休眠。如果注释掉(1)、(2),则运行结果有多种可能性,可能性之一(概率较小),符合正常逻辑:
B取钱成功,吐出钞票:800.0
余额为 : 200.0
A取钱失败,余额不足!
应该是B先强找到取钱资源,并且正确修改余额后,A才开始判断用户余额;这种概率非常小,多数运行会类似以下情况:
A取钱成功,吐出钞票:800.0
B取钱成功,吐出钞票:800.0
余额为 : -600.0
余额为 : 200.0
这明显是不符合逻辑的,从运行结果可以猜测,A先抢占资源,取出金额,但在修改余额之前,资源被B抢占;由于余额未被修改,则B看到余额仍然是800,B仍然取出金额;A先运行修改余额,但并未打印,B抢夺资源;B修改余额,并打印余额,为-600;A打印余额,为200;
如果加上(2)线程休眠,则一定是错误状况,因为A或B在取出金额后一定会因为sleep释放CPU资源,JVM会调用其他处于准备状态的进程。第二个取钱判断余额一定是错误的。
如果加上(1)synchronized同步代码块,在线程run方法体中对account进行加锁;则每次都会保证执行逻辑正常:
A取钱成功,吐出钞票:800.0
余额为 : 200.0
B取钱失败,余额不足!
可以设想一下执行过程:
A先抢占资源,在run方法体初始对account类进行加锁;然后开始执行同步代码块;如果执行到中间某个环节,CPU资源被B抢占;B开始执行,一开始也会对account类进行加锁。但是加锁时会发现account已经被A占用,则会调整为阻塞状态等待A释放资源;A执行完同步代码块后释放account的锁,B继续执行;B运行时看到的余额保证是A已经修改过的,会按照正确逻辑正常执行。
来源:http://blog.csdn.net/sherry_rui/article/details/51073114


猜你喜欢
- 在实践中,项目的某些配置信息是需要进行加密处理的,以减少敏感信息泄露的风险。比如,在使用Druid时,就可以基于它提供的公私钥加密方式对数据
- 本文实例分析了C#动态生成DropDownList执行失败原因。分享给大家供大家参考。具体如下:今天研究DDL控件的动态生成的时候遇到了点问
- 昨天看了一段android配置aspectj实现AOP的直播视频,就试着自己配置了一下,可能是因为我自己的AndroidStudio环境的问
- 本文实例为大家分享了OpenGL绘制贝塞尔曲线的具体代码,供大家参考,具体内容如下最终效果图:通过3个点形成一条贝塞尔曲线1. 鼠标问题在使
- 首先给大家介绍一文件的上传 实体类import java.sql.Timestamp; /** * * @Decription 文件上传实体
- 前言Feign是Netflix开源的声明式HTTP客户端,致力于让编写http client更加简单,Feign可以通过声明接口自动构造请求
- 一、Springboot项目运行正常打包前,Springbooot项目在本地必须是运行正常的。我们这里使用本专栏写起来的项目,如下所示:来访
- 一、reservedcodecachesize参数介绍该参数是JvM虚拟机调优中调整内存大小的一个设置参数,值得大小设置直接影响到Code
- 以最终客户的角度来看,JAR文件就是一种封装,他们不需要知道jar文件中有多少个.class文件,每个文件中的功能与作用,同样可以得到他们希
- 本文分析了Android中EditText光标在4.0中的bug及解决方法。分享给大家供大家参考,具体如下:一、问题:不知道为什么,我的Ed
- 以往都是使用反射调用实例方法,那么反射如何调用静态方法呢?看下面的例子Class<?> threadClazz = Class.
- 本文实例讲述了 Android 7.0开发获取存储设备信息的方法。分享给大家供大家参考,具体如下:Android 7.0开发相较之前有不少改
- using System;using System.Collections.Generic;using System.IO;using Sy
- 由于 Spring 拥有对象的管理权,所以我们也需要拥有较为高效的对象存储和取出的手段,下面我们来分别总结一下:存对象配置文件在存储对象之前
- 一、基本定义Arrays类,全路径java.util.Arrays,主要功能为操作数组,Arrays类的所有方法均为静态方法,所以调用方式全
- java连接zookeeper实现zookeeperJava服务端连接Zookeeper,进行节点信息的获取,管理…整理成一个基本工具添加依
- 本文使用的Unicode+DLL+Debug的方式,因为不想最后生成的exe文件太大。环境搭建步骤如下:1、下载wxWidgets包:登录w
- 工厂方法模式定义: Define an interface for creating an object, but let subclass
- 美团电商应用平台大家使用非常频繁,下面小编通过本文给大家介绍电商应用平台中常用的选择类别下拉列表的实现。先给大家展示下效果图:一、下拉列表的
- 一、多媒体应用架构1.1 音视频传统应用架构通常,传统的播放音频或视频的多媒体应用由两部分组成:播放器:用于吸收数字媒体并将其呈现为视频和/