Java同步不起作用(I)预期

时间:2009-06-20 02:08:40

标签: java multithreading synchronization

程序员队友。我用一个非常简单的代码测试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,但有时候不会发生这种情况。我真诚地想不出原因。有人能看出我的错吗?

6 个答案:

答案 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());

如果你打断主线程,那么你需要中断异常的事情。假设您的代码都没有这样做,您应该始终至少记录异常。注意:如果有人中断InterruptedExceptionc1,您将获得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)

您会注意到撤销与添加负数相同,因此您并不真正需要它。

金额应该是私有的,不受保护,因为您可以对其执行的所有操作都应在“帐户”中定义并同步。