Java 简单现金存取程序中的多线程处理
我的导师说使用多线程更新帐户管理系统。下面给出了系统的大致概念。 这是我的源代码 科目类别Java 简单现金存取程序中的多线程处理,java,multithreading,synchronized,Java,Multithreading,Synchronized,我的导师说使用多线程更新帐户管理系统。下面给出了系统的大致概念。 这是我的源代码 科目类别 public class Account { int balance= 1000; public int getBal(){ return balance; } public void withdraw(int bal){ balance= balance-bal; } public void deposit(int
public class Account {
int balance= 1000;
public int getBal(){
return balance;
}
public void withdraw(int bal){
balance= balance-bal;
}
public void deposit(int bal){
balance= balance+bal;
}
}
public class ThreadExercise implements Runnable{
Account acc = new Account();
public static void main(String[] args) {
ThreadExercise ts = new ThreadExercise();
Thread t1 = new Thread(ts, "person 1");
Thread t2 = new Thread(ts, "person 2");
Thread t3 = new Thread(ts, "person 3");
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
makeWithdraw(100);
if (acc.getBal() < 0) {
System.out.println("account is overdrawn!");
}
deposit(200);
}
}
private synchronized void makeWithdraw(int bal){
if (acc.getBal()>=bal) {
System.out.println(Thread.currentThread().getName()+" "+ "is try to withdraw");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
acc.withdraw(bal);
System.out.println(Thread.currentThread().getName()+" "+ "is complete the withdraw");
}else{
System.out.println(Thread.currentThread().getName()+ " "+"doesn't have enough money for withdraw ");
}
}
private synchronized void deposit(int bal){
if (bal>0) {
System.out.println(Thread.currentThread().getName()+" "+ " is try to deposit");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
acc.deposit(bal);
System.out.println(Thread.currentThread().getName()+" "+ "is complete the deposit");
}else{
System.out.println(Thread.currentThread().getName()+ " "+"doesn't have enough money for deposit");
}
}
}
ThreadExercise类
public class Account {
int balance= 1000;
public int getBal(){
return balance;
}
public void withdraw(int bal){
balance= balance-bal;
}
public void deposit(int bal){
balance= balance+bal;
}
}
public class ThreadExercise implements Runnable{
Account acc = new Account();
public static void main(String[] args) {
ThreadExercise ts = new ThreadExercise();
Thread t1 = new Thread(ts, "person 1");
Thread t2 = new Thread(ts, "person 2");
Thread t3 = new Thread(ts, "person 3");
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
makeWithdraw(100);
if (acc.getBal() < 0) {
System.out.println("account is overdrawn!");
}
deposit(200);
}
}
private synchronized void makeWithdraw(int bal){
if (acc.getBal()>=bal) {
System.out.println(Thread.currentThread().getName()+" "+ "is try to withdraw");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
acc.withdraw(bal);
System.out.println(Thread.currentThread().getName()+" "+ "is complete the withdraw");
}else{
System.out.println(Thread.currentThread().getName()+ " "+"doesn't have enough money for withdraw ");
}
}
private synchronized void deposit(int bal){
if (bal>0) {
System.out.println(Thread.currentThread().getName()+" "+ " is try to deposit");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
acc.deposit(bal);
System.out.println(Thread.currentThread().getName()+" "+ "is complete the deposit");
}else{
System.out.println(Thread.currentThread().getName()+ " "+"doesn't have enough money for deposit");
}
}
}
public类ThreadExercise实现可运行{
账户acc=新账户();
公共静态void main(字符串[]args){
ThreadExercise ts=新的ThreadExercise();
线程t1=新线程(ts,“人1”);
螺纹t2=新螺纹(ts,“人员2”);
螺纹t3=新螺纹(ts,“人员3”);
t1.start();
t2.start();
t3.start();
}
@凌驾
公开募捐{
对于(int i=0;i<3;i++){
makedraw(100);
如果(根据getBal()<0){
System.out.println(“帐户透支!”);
}
存款(200);
}
}
专用同步void makedraw(int-bal){
如果(根据getBal()>=bal){
System.out.println(Thread.currentThread().getName()+“”+“正在尝试撤消”);
试一试{
睡眠(100);
}捕获(例外e){
e、 printStackTrace();
}
acc.draw(bal);
System.out.println(Thread.currentThread().getName()+“”+“完成撤消”);
}否则{
System.out.println(Thread.currentThread().getName()+“”+“没有足够的钱取款”);
}
}
私人同步作废存款(整数余额){
如果(bal>0){
System.out.println(Thread.currentThread().getName()+“”+“正在尝试存放”);
试一试{
睡眠(100);
}捕获(例外e){
e、 printStackTrace();
}
账户存款(bal);
System.out.println(Thread.currentThread().getName()+“”+“完成存款”);
}否则{
System.out.println(Thread.currentThread().getName()+“”+”没有足够的存款);
}
}
}
代码运行良好。但我真的认为这段代码缺失了一些东西。你能帮我找出那个毛病吗
在ThreadExecute类中同步makeDraw()和deposit()方法还不够吗?我是否应该删除该同步并同步Account类中的Draw()和deposit()。请给我一个清楚的想法。
谢谢你的支持 考虑到设计,Account类必须同步(其中的方法) 目前,其他人可能会将实例检索到帐户,并以线程不安全的方式使用它。在这种情况下,只需从其他地方调用Account的方法就会使它崩溃
public class Account {
private final Object lock = new Object();
// Must be private to be thread-safe!
private int balance= 1000;
public int getBal(){
return balance;
}
public synchronized void withdraw(int bal){
synchronized (lock) {
balance= balance-bal;
}
}
public synchronized void deposit(int bal){
synchronized (lock) {
balance= balance+bal;
}
}
}
您的帐户类不是线程安全的。虽然您已经同步了ThreadExecution类的存款和取款方法,但存款/取款锁定线程时,可以更改基础余额 考虑场景 线程1调用ThreadExercise.deposit,它检查余额并等待。 线程2同时唤醒并更新余额 因此,您的帐户余额实际上并没有与并发的存款+取款通话同步 您可以如下定义余额
AtomicInteger balance = new AtomicInteger(1000);
那么提取方法可以写如下
public boolean withdraw (int amtToWithdraw, int existingBalance){
return balance.compareAndSet(existingBalance,existingBalance-amtToWithdraw);
}
public void deposit(int amtToDeposit, int existingBalance){
return balance.compareAndSet(existingBalance,existingBalance+amtToDeposit);
}
您可能需要处理故障情况。我不确定其他答案是否清楚 您已经同步了
ThreadExercise
类上的方法。
这意味着一次只能有一个线程在给定的ThreadExercise
对象上调用这些方法。
这没有效果,因为每个线程对象无论如何只会调用一个这样的对象上的方法
您需要同步Account
类的方法,以确保一次只有一个线程在任何给定的Account
上调用一个方法
当然,在任何实际系统中,Account
对象都会(以某种方式)序列化到某个数据库或对象存储中,并且您需要确保您的应用程序没有引入表示一个“物理”帐户的两个“doppelganger”对象。在具有多个ATM交换机的分布式系统上,这可能很棘手
如果引入平衡转移的概念,则可能需要引入进一步的同步。如果某个观察员进程无法接受以下情况,则尤其如此:
Account 1: $100
Account 2: $0
Account 1: $40
Account 2: $0
Account 1: $40
Account 2: $60
其中一笔60美元的转账消失并重新出现。
那里没有业务问题。银行从坐在上面的X那里拿钱,然后把钱传给Y,赚了数百万,这和他们榨取客户的钱没有什么好的理由。
我只是想指出,向方法添加synchronized
并不是并发编程的全部答案
我曾经看到一个组织,成功地执行了这样一种转移,在中间有错误,使帐户处于状态:
Account 1: $100
Account 2: $60
从帐户1到帐户2的60美元(1000万美元IRL)到达,但从未离开!那是另一个故事
然而,要回答这个问题:
public class Account {
int balance= 1000;
public int getBal(){
return balance;
}
public synchronized void withdraw(int bal){
balance= balance-bal;
}
public synchronized void deposit(int bal){
balance= balance+bal;
}
}
我挑衅地没有同步getBal()
。一方面,int
读取和写入是原子的,因此它总是读取一致的balance
值,而不是(比如)写入操作只更新低字节的“混蛋”。如果将其更改为long
,JVM将不再保证这一点
但是,不同步意味着您可以看到异常位置:
Account 1: $100
Account 2: $60
即使您的代码是:
account1.withdraw(60);
account2.deposit(60);
这是因为同步不仅会引入阻塞,还会影响内存屏障。假设一个单独的线程缓存了account1
,但没有account2
,如果没有同步,它不会知道account1
已过时,但是
public class ThreadExercise extends Thread implements Runnable {
private Person person;
public ThreadExercise(Person p) {
this.person = p;
}
public static void main(String[] args) {
ThreadExercise ts1 = new ThreadExercise(new Person("person 1"));
ts1.start();
ThreadExercise ts2 = new ThreadExercise(new Person("person 2"));
ts2.start();
ThreadExercise ts3 = new ThreadExercise(new Person("person 3"));
ts3.start();
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
try {
Account acc = Account.getAccount(person);
acc.withdraw(100);
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
Logger.getLogger(ThreadExercise.class.getName()).log(Level.SEVERE, null, ex);
}
if (acc.getBal() < 0) {
System.out.println("account is overdrawn!");
}
acc.deposit(200);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Final Acc balance is Rs." + Account.getBal());
}}