程序员队友。我用一个非常简单的代码测试java线程功能(或者至少它似乎简单)。我有这个班级帐号:
public class Account {
protected double balance;
public synchronized void withdraw(double value) {
this.balance = this.balance - value;
}
public synchronized void deposit(double value) {
this.balance = this.balance + value;
}
public synchronized double getBalance() {
return this.balance;
}
}
我有两个主题:Depositer
,每次存款10美元:
public class Depositer extends Thread {
protected Account account;
public Depositer(Account a) {
account = a;
}
@Override
public void run() {
for(int i = 0; i < 1000; i++) {
this.account.deposit(10);
}
}
}
Withdrawer
,一次性提取10美元:
public class Withdrawer extends Thread {
protected Account account;
public Withdrawer(Account a) {
account = a;
}
@Override
public void run() {
for(int i = 0; i < 1000; i++) {
this.account.withdraw(10);
}
}
}
这种安排由以下人员执行:
public class Main {
public static void main(String[] args) {
Account account = new Account();
Thread c1 = new Depositer(account);
Thread c2 = new Withdrawer(account);
c2.start();
c1.start();
System.out.println(account.getBalance());
}
}
由于方法是同步的,我只是期望最后的余额总是为0,但有时候不会发生这种情况。我真诚地想不出原因。有人能看出我的错吗?
答案 0 :(得分:6)
c2.start()
和c1.start()
异步运行该进程。在打印出结果之前,你的主线程需要等待这两个线程完成。
致电
try {
c2.join();
c1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
在致电println之前。
请参阅join
。
等待此线程死亡。
答案 1 :(得分:5)
您执行以下操作:
c2.start(); // Start a thread in the background
c1.start(); // Start a 2nd thread in the background
// print out the balance while both threads are still running
System.out.println(account.getBalance());
您需要等待这些线程完成处理:
c2.start(); // Start a thread in the background
c1.start(); // Start a 2nd thread in the background
try {
c2.join(); // Wait until the c2 thread completes
c1.join(); // Wait until the c1 thread completes
} catch (InterruptedException e) {
// LOG AN ERROR HERE
}
// print out the final balance
System.out.println(account.getBalance());
如果你打断主线程,那么你需要做中断异常的事情。假设您的代码都没有这样做,您应该始终至少记录异常。注意:如果有人中断InterruptedException
或c1
,您将获得c2
,但如果有人中断您的主线程,则会调用{{1 }}。如果有人在您的主要主题上调用join()
但您没有检查它,那么您可能会在致电interrupt()
时获得InterruptedException
。
答案 2 :(得分:2)
在检查“最终余额”之前,您必须等待两个线程完成。
c1.join();
c2.join();
System.out.println(account.getBalance());
答案 3 :(得分:2)
在多线程代码中,“结束”的定义很棘手。
同步仅表示帐户上的每个操作都是互斥的。也就是说,在您存入,取出或查看余额时,帐户不会更新。
然而,在产生线程之后,您可以在主程序中打印出天平。由于它们是独立的线程,因此无法保证它们在您进入打印之前完成运行。因此,您可以在计数到1000之前进行打印。或者换句话说,最后总和为0,但最后不打印...
您可能想要加入main中的主题。
答案 4 :(得分:1)
这种类型的并发错误称为race condition。你需要synchronize线程。通过使用其他人解释的join
方法,您将有效地实现并发Barrier。
答案 5 :(得分:0)
您会注意到撤销与添加负数相同,因此您并不真正需要它。
金额应该是私有的,不受保护,因为您可以对其执行的所有操作都应在“帐户”中定义并同步。