日期:2014-05-20  浏览次数:20971 次

关于多线程读取数据库记录出现的问题
线程的run()部分如下
Java code
public void run() {
        System.out.println(_threadName + " starts to run.");
        float updatedValue = 0;
        try {
            
            Connection conn = DatabaseHelper.getConnection();
            updatedValue = _account.getBalance() + _amount;
            System.out.println(_threadName + " before:" + _account.getBalance());
            System.out.println(_threadName + " sleep.");
            Thread.sleep(_delayedTime);
            System.out.println(_threadName + " wake up.");
            System.err.println(_threadName + " 1 balance = " + _account.getBalance());
            _account.setBalance(updatedValue);
            System.err.println(_threadName + " 2 balance = " + _account.getBalance());
            System.out.println(_threadName + " after:"  + _account.getBalance());
            conn.commit();
            conn.close();
        
        } catch (Exception e) {    
            e.printStackTrace();
        }
        System.out.println(_threadName + " stops running here.");
    }

两个线程同时运行,线程1的_delayTime是3秒,线程2的_delayTime是1秒,我确定让线程2运行完,也就是在线程1对记录更新前,线程2已更新过。很明显,我是在模拟在没有锁的情况下记录操作的错误情况。问题是,当线程1醒来之后,“System.err.println(_threadName + " 1 balance = " + _account.getBalance());”输出的记录却是线程2更新的值,我曾延长过_delayTime去看数据库中的记录,确实已经被线程2更新。

问题:为什么会出现这种情况?怎样修改才能让线程1中的“System.err.println(_threadName + " 1 balance = " + _account.getBalance());”输出的记录却是线程2更新的值?先谢谢大家了。

------解决方案--------------------
_account对象没有同步,线程2退出时,线程1的_account还是线程1的工作内存中的值。所以_account.getBalance()还是原来的的值,只有对_account对象同步,才能保证线程2退出时,它更新的值才会写到主内存中,而线程2进入同步块前也会从主内存复制新值到自己的工作内存中。

没有同步时,不同JVM实现 的行为不一样,有的可能也会从回写到方内存和从主内存复制,但一般为了优化,如果没有同步,会直接从自己的工作内存拿数据,如果编译器足够聪明,中间过程根本不复制,只保证最后结果回写。

比如在多线程中,其中一个线程 for(int i=0;i<100;i++) x ++;
这个线程不会每把x+1都让其它线程看到,也就是你用另一个线程看x的值,并不是看到一直加了100次,那个线程会等到100次加完后把最后的值写到主内存中,其它线程直接看到最后的结果。

但如果是在同步块中,只要离开同步块,新值就一定会写到主内存中,其它线程在进入同步前也一会从主内存重取一次。
------解决方案--------------------
http://blog.csdn.net/axman/archive/2006/08/19/1097541.aspx