2 wo317458719 wo317458719 于 2016.03.22 23:10 提问

多线程同步问题,请高手帮忙分析一下原因

先上代码

public class Banck {
private Double[] accounts;
public Banck(int n,Double inintPrice) {
    accounts= new Double[n];
    for(int i=0;i<n;i++){
        accounts[i]=inintPrice;
    }
}


public  void transfer(Integer from,Integer to,Double mouny) throws InterruptedException{
    synchronized(accounts){
    while(accounts[from]<mouny){
        return;
    }
    System.out.print("线程"+Thread.currentThread());
    accounts[from]-=mouny;
    System.out.printf(" 金额 %10.2f 从账户  %d 到  %d",mouny,from,to);
    accounts[to]+=mouny;
    System.out.printf(" 总金额 %10.2f",getTotal());
    System.out.println();
    }
}

public  Double getTotal() throws InterruptedException{
    double total=0d;
    synchronized(accounts){
    for( int i=0;i<accounts.length;i++){
        total+=accounts[i];
    }
    }
    return total;
}
public int size(){
    return accounts.length;
}
public Double[] getAccounts() {
    return accounts;
}

public void setAccounts(Double[] accounts) {
    this.accounts = accounts;
}

}

#############################
public class TransferRunnable implements Runnable{
private Banck banck;
private Double amount;
private int fromBank;
public TransferRunnable(Banck bank,int from , Double amount){
this.banck=bank;
this.fromBank=from;
this.amount=amount;
}
@Override
public void run() {
synchronized(this){
while(true){
int toBanck=(int)(banck.size()*Math.random());
Double mouny=amount*Math.random();
try {
banck.transfer(fromBank, toBanck, mouny);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
System.out.println("---"+banck.getTotal());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public Banck getBanck() {
return banck;
}
public void setBanck(Banck banck) {
this.banck = banck;
}
public int getFromBank() {
return fromBank;
}
public void setFromBank(int fromBank) {
this.fromBank = fromBank;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}

}

############################################
public class ThreadTest {

public static void main(String[] args) {
    Banck b=new Banck(10,100d);
    for(int i=0;i<b.size();i++){
        TransferRunnable tr=new TransferRunnable(b,i,100d);
        Thread t=new Thread(tr);
        t.start();
    }
}

}

运行结果:
线程Thread[Thread-3,5,main] 金额 62.13 从账户 3 到 4 总金额 1000.00
---1000.0
线程Thread[Thread-4,5,main] 金额 13.78 从账户 4 到 2 总金额 1000.00
---1000.0
线程Thread[Thread-5,5,main] 金额 6.86 从账户 5 到 5 总金额 1000.00
---1000.0
线程Thread[Thread-9,5,main] 金额 15.11 从账户 9 到 6 总金额 1000.00
---1000.0
线程Thread[Thread-1,5,main] 金额 4.90 从账户 1 到 3 总金额 1000.00
---1000.0000000000001
线程Thread[Thread-8,5,main] 金额 46.37 从账户 8 到 0 总金额 1000.00
---1000.0
---1000.0
线程Thread[Thread-0,5,main] 金额 50.18 从账户 0 到 3 总金额 1000.00
---1000.0
线程Thread[Thread-6,5,main] 金额 19.87 从账户 6 到 7 总金额 1000.00
---999.9999999999999
线程Thread[Thread-7,5,main] 金额 1.95 从账户 7 到 7 总金额 1000.00
---999.9999999999999
线程Thread[Thread-3,5,main] 金额 34.37 从账户 3 到 8 总金额 1000.00
---999.9999999999999
---999.9999999999999
线程Thread[Thread-5,5,main] 金额 84.46 从账户 5 到 3 总金额 1000.00

我想知道,在run方法里面调用Banck类的getTotal()方法,为什么会变。我加了同步锁为什么不启作用?该怎么解决?

2个回答

devmiao
devmiao   Ds   Rxr 2016.03.22 23:56

同步的调用是不是有问题,调试下。

wojiushiwo945you
wojiushiwo945you   Ds   Rxr 2016.03.23 11:40

首先,你的TransferRunnable类中使用的同步块为synchronized(this)也就是以每个线程任务对象为锁,所以你所有的线程之间都是使用的自己线程对象锁,当然run中对共享变量的访问不是互斥的。修正使用相同的锁对象,如下:TransferRunnable的run中锁修正

 @Override
    public void run() {
        synchronized (banck) {
        }
        }

还有一个问题是,你的线程的run方法中使用了while(true)死循环,所以,线程将一直运行下去,无法终结。你的业务还需要仔细分析下,什么时候循环结束呢?
多线程同步问题中,需要明确一点原则:就是对共享变量的访问要保证使用的是相同的锁对象,才能保证共享对象的数据一致性。举个简单的例子,你用synchronized(this)就好比使用卫生间,使用的是自己家的卫生间,那么不同人都使用的是自己家的卫生间即各自自己的锁,当然不存在互斥访问问题了。但是,如果是火车上的卫生间,是公共的,那么使用者必须获得使用权,即同一个锁对象,才存在互斥访问、排队等待的现象。

Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!