public class TransferRunnable implements Runnable {
public TransferRunnable(Bank b,int from,double max){
bank = b;
fromAccount = from;
maxAmount = max;
}
public void run(){
while(true){
//fromAccount =1,2,3,4,5
int toAccount = (int)(bank.size()*Math.random());
double amount = maxAmount*Math.random();
bank.transfer(fromAccount,toAccount,amount);
try {
Thread.sleep((int)(DELAY*Math.random()));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private Bank bank;
private int fromAccount;
private double maxAmount;
private int DELAY =10;
}
package com.trx.demothread;
public class UnsynchBankTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Bank b =new Bank(NACCOUNTS,INITIAL_BALANCE);
int i;
for(i=0;i<NACCOUNTS;i++){
TransferRunnable r =new TransferRunnable(b,i,INITIAL_BALANCE);
Thread t = new Thread(r);
t.start();
}
}
public static final int NACCOUNTS = 100;
public static final double INITIAL_BALANCE=1000;
}
详解竞争条件
几个线程更新银行账户余额。一段时间后,错误不知不觉地出现了,总额要么增加,要么变少。当两个线程试图同时更新同一个账户的时候,这个问题就出现了。假定两个线程同时执行指令
accounts[to] + = amount;
问题在于这不是原子操作。该指令可能被处理如下:
1)将accounts[0]加载到寄存器。
2)增加amount.
3)将结果写回accounts[to]。
现在,假定第一个线程执行步骤1和2,然后,它被剥夺了运行权。假定第二个线程被唤醒并修改了accounts数组中的同一项。然后,第一个线程被唤醒并完成其第3步。
这样,这一动作擦去了第二个线程所做的更新。于是,总金额不在正确。
加入synchronized或 Lock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Bank {
private Lock bankLock = new ReentrantLock();
private Object o = new Object();
public Bank(int n,double initialBalance){
accounts = new double[n];
for(int i=0;i<accounts.length;i++)
accounts[i] = initialBalance;
}
public void transfer(int from,int to,double amount){
// synchronized(o){
try{
bankLock.lock();
if(accounts[from]<amount) return;
System.out.print(Thread.currentThread());
accounts[from]-=amount;
System.out.printf(" %10.2f from %d to %d",amount,from,to);
accounts[to]+=amount;
System.out.printf(" Total Balance: %10.2f%n",getTotalBalance());
}
finally
{
bankLock.unlock();
}
// }
}
public double getTotalBalance(){
double sum=0;
for(double a:accounts)
sum+=a;
return sum;
}
public int size(){
return accounts.length;
}
private final double[] accounts;
}