java中的同步给出了意外的输出

时间:2017-03-03 10:15:39

标签: java multithreading synchronization thread-safety

我正在阅读一本书并看到一段代码,该代码表明下面的代码是线程同步的:

//帐户类

 public class Account {

 private double balance;

 public double getBalance() {
 return balance;
}

 public void setBalance(double balance) {
 this.balance = balance;
}


 public synchronized void addAmount(double amount) {
 double tmp=balance;
 try {
 Thread.sleep(10);
 } catch (InterruptedException e) {
   e.printStackTrace();
 }
 tmp+=amount;
 balance=tmp;
}


public synchronized void subtractAmount(double amount) {
  double tmp=balance;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
tmp-=amount;
balance=tmp;
}

}

//银行班

public class Bank implements Runnable {

 private Account account;
 public Bank(Account account) {
  this.account=account; 
}

@Override

public void run() {

for (int i=0; i<1000; i++){

     account.subtractAmount(1000);
  }
 }
}

//公司类

public class Company implements Runnable {

 private Account account;

 public Company(Account account) {
    this.account=account;
}

@Override

 public void run() {
   for (int i=0; i<1000; i++){
   account.addAmount(1000);
   }
  }
}

//主要课程

 public class Main {
 public static void main(String[] args) {
 Account account=new Account();
 account.setBalance(1000);
 Company company=new Company(account);
 Thread companyThread=new Thread(company);
 Bank bank=new Bank(account);
 Thread bankThread=new Thread(bank);
 System.out.printf("Account : Initial Balance: %f\n",account.
 getBalance());
 companyThread.start();
 bankThread.start();

 try {
   companyThread.join();
   bankThread.join();
   System.out.printf("Account : Final Balance: %f\n",account.
   getBalance());
 } 
    catch (InterruptedException e) {
    e.printStackTrace();
  }
 }
}

这里两个不同对象的两个线程正在使用addAmount和subtractAmount函数同时更改平衡变量,但它始终给出正确的输出作为初始和最终余额相同,即1000.000。我的意思是他们应该有一些问题,因为两个线程同时处理同一个变量。

请纠正我的错误。 提前谢谢

2 个答案:

答案 0 :(得分:0)

这是同步的全部要点。它确保一次只有一个线程可以执行该功能。

答案 1 :(得分:0)

Java中发生的事情是每个对象都有一个intrisinc锁,并且使用synchronize关键字表示当一个Thread(让我们称之为A)调用该方法时,线程A持有锁。当线程A持有锁时,其他线程将被阻止访问Object的同步部分(在您的情况下为Account对象)。

一旦线程A完成执行同步块(或本例中的方法),线程A就会释放对象的锁定,而等待访问您的Account对象的其他线程现在可以继续前进。

  
      
  • 首先,对同一对象的两个同步方法的调用不可能进行交错。当一个线程正在执行时   对象的同步方法,所有其他调用的线程   同一对象块的同步方法(暂停执行)   直到第一个线程完成对象。
  •   
  • 其次,当同步方法退出时,它会自动建立与之后的任何关系   调用同一对象的同步方法。这个   保证所有人都可以看到对象状态的变化   线程。
  •   

有关使用synchronized的详细信息,请参阅https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

实际上为了使Account线程安全,您可能希望将setter同步并将余额声明为volatile:

public class Account {

 private volatile double balance;

 public double getBalance() {
 return balance;
 }

 public synchronized void setBalance(double balance) {
 this.balance = balance;
}

//其余代码已经同步    }

有关易失性用途的更多信息,请参阅https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html