线程的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