Java 如何在简单的现金存款提取程序中处理多线程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29364771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle multithreading in simple cash deposit withdraw program
提问by Search Results Web results Pi
My instructor said to use multi-threading for update an account management system. Given below is a rough idea of the system.
我的导师说使用多线程来更新帐户管理系统。下面给出了该系统的粗略概念。
Here is my source code for it.
这是我的源代码。
Account class
账户类
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;
}
}
ThreadExercise class
线程练习类
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");
}
}
}
Code is working fine. But I really think something is missing this code. Can you please help me for finding that fault.
代码工作正常。但我真的认为有些东西缺少此代码。你能帮我找出那个错误吗?
Is it not be enough synchronizing the makeWithdraw() and deposit() methods in ThreadExercise class and should I remove that synchronizing and synchronize the withdraw() and deposit() in Account class. Please give me a clear idea.
Thank you for your support.
同步 ThreadExercise 类中的 makeWithdraw() 和 deposit() 方法是否还不够,我是否应该删除该同步并同步 Account 类中的withdraw() 和 deposit()。请给我一个明确的想法。
感谢您的支持。
采纳答案by Isuru Srimal
Account class
账户类
public class Account {
public static Account account;
private static int balance = 1000;
private static Person person;
private Account() {
}
public static Account getAccount(Person p) {
if (account == null) {
account = new Account();
}
Account.person = p;
return account;
}
public static int getBal() {
return balance;
}
public synchronized void withdraw(int bal) {
try {
if (balance >= bal) {
System.out.println(person.getName() + " " + "is try to withdraw");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
balance = balance - bal;
System.out.println(person.getName() + " " + "is complete the withdraw");
} else {
System.out.println(person.getName() + " " + "doesn't have enough money for withdraw ");
}
System.out.println(person.getName() + " " + " withdraw Rs." + balance);
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void deposit(int bal) {
try {
if (bal > 0) {
System.out.println(person.getName() + " " + " is try to deposit");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
balance = balance + bal;
System.out.println(person.getName() + " " + "is complete the deposit");
} else {
System.out.println(person.getName() + " " + "doesn't have enough money for deposit");
}
System.out.println(person.getName() + " " + " deposit Rs." + balance);
} catch (Exception e) {
e.printStackTrace();
}
}}
Person class
人物类
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}}
ThreadExercise class
线程练习类
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());
}}
回答by Sebastian
Considering the design the Account class must be synchronized (the methods in it).
考虑到设计,必须同步 Account 类(其中的方法)。
The way it currently is someone else may retrieve an instance to an account and use it in a manner which is not thread-safe. In this case simply invoking the Account'methods from somewhere else would beak it.
当前的方式是其他人可以将实例检索到帐户并以非线程安全的方式使用它。在这种情况下,只需从其他地方调用 Account'methods 即可。
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;
}
}
}
回答by Himanshu Ahire
You Account class is not thread safe. Although you have synchronized the Deposit & withdraw methods of ThreadExercise class, the underlying balance can be changed while the deposit / withdraw has locked the thread.
您的 Account 类不是线程安全的。虽然您已经同步了 ThreadExercise 类的存款和取款方法,但可以在存款/取款锁定线程时更改基础余额。
Consider Scenario
考虑场景
Thread 1 calls ThreadExercise.deposit it checks the balance and wait. The same time Thread 2 wakes up and update the balance.
线程 1 调用 ThreadExercise.deposit 它检查余额并等待。同时线程2唤醒并更新余额。
So you account balance is not really synchronized against concurrent deposit + withdraw calls.
因此,您的帐户余额并未真正与并发存款 + 取款电话同步。
You can define the balance as below.
您可以定义余额如下。
AtomicInteger balance = new AtomicInteger(1000);
Then the withdraw method can be written as below
那么提款方法可以写成如下
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);
}
You may need to handle the failure scenario.
您可能需要处理故障情况。
回答by Persixty
I'm not sure the other answers have been clear.
我不确定其他答案是否清楚。
You've synchronized
the methods on the ThreadExercise
class.
That means only one thread can invoke those methods on a given ThreadExercise
object at once.
That has no effect because each thread object will only invoke methods on one such object anyway.
你有课堂synchronized
上的方法ThreadExercise
。这意味着只有一个线程可以一次调用给定ThreadExercise
对象上的这些方法。这没有任何效果,因为无论如何每个线程对象只会调用一个这样的对象上的方法。
You need to synchronize
the methods of the Account
class to make sure only one thread is invoking one method on any given Account
at a time.
您需要synchronize
使用Account
类的方法来确保一次只有一个线程在任何给定的方法上调用一个方法Account
。
Of course in any real system Account
objects would be (somehow) serialized to some database or object store and you would need to make sure that your application didn't introduce two 'doppelganger' objects that represent one 'physical' account. That might be tricky on a distributed system with multiple ATM Switches.
当然,在任何真实的系统中,Account
对象都会(以某种方式)序列化到某个数据库或对象存储,您需要确保您的应用程序没有引入两个代表一个“物理”帐户的“分身”对象。在具有多个 ATM 交换机的分布式系统上,这可能会很棘手。
If you introduce the idea of a balance transfer you might need to introduce further synchronization. That's particularly true if it was unacceptable for some observer process to see:
如果您引入余额转移的想法,您可能需要引入进一步的同步。如果某些观察者进程看到是不可接受的,则尤其如此:
Account 1: 0
Account 2: Account 1: 0
Account 2:
Account 1:
Account 2: 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;
}
}
Account 1:
Account 2:
In which a $60 transfer is seen to disappear and re-appear.
There's no business problem there. Banks make millions taking money from X sitting on it and then passing it on to Y for no good reason than they can milk their clients.
I'm just making the point that adding synchronized
to methods isn't the whole answer to concurrent programming.
其中 60 美元的转账消失并重新出现。那里没有业务问题。银行从 X 那里拿钱赚了数百万美元,然后无缘无故地把钱转给 Y,因为他们可以挤奶给他们的客户。我只是想说明添加synchronized
方法并不是并发编程的全部答案。
I did once see an organization that managed to execute such a transfer and have an error in the middle and leave the accounts in the state:
我曾经看到一个组织成功地执行了这样的转移并且在中间出现了错误并且将帐户保持在状态:
Account 1: 0
Account 2:
Where a $60 ($10 millions IRL) from Account 1 to 2 arrived but never left! That's another story...
从账户 1 到账户 2 的 60 美元(1000 万美元 IRL)到达但从未离开!那是另一个故事了...
However to answer the question:
但是要回答这个问题:
account1.withdraw(60);
account2.deposit(60);
I have provocatively not synchronized getBal()
. On the one hand int
reads and writes are atomic so it will always read a consistent value of balance
and not some 'bastard' where (say) a write operation has only updated the low bytes. If you changed it to long
the JVM doesn't make that guarantee anymore.
我已经挑衅不同步getBal()
。一方面,int
读取和写入是原子的,因此它将始终读取一致的值,balance
而不是某些“混蛋”,其中(例如)写入操作仅更新了低字节。如果您将其更改为long
JVM,则不再提供该保证。
However not synchronizing it means you could see that anomalous position:
但是不同步它意味着您可以看到异常位置:
##代码##The could occur even if your code was:
即使您的代码是:
##代码##That's because synchronization doesn't just introduce blocking but also effects a memory barrier. Suppose a separate thread had account1
cached but not account2
without synchronization it wouldn't know account1
was stale but fetch an up to date version of account2
.
这是因为同步不仅会引入阻塞,还会影响内存屏障。假设一个单独的线程已经account1
缓存但不是account2
没有同步,它不会知道account1
是陈旧的,而是获取account2
.
It's worth a footnote that your class is so simple you could get away with using java.util.concurrent.atomic.AtomicInteger
using addAndGet(int delta)
.
However as soon as you start adding a sophistication (such as an overdraft limit) you'll need to go back to synchronization.
值得一提的是,您的类非常简单,您可以使用java.util.concurrent.atomic.AtomicInteger
using逃脱addAndGet(int delta)
。但是,一旦您开始添加复杂性(例如透支限制),您就需要返回同步。