从单条指令级上分析Java多线程对数据操作为何出错(源代码源自雍俊海的Java教程。
//
// J_ThreadSum.java
//
// 开发者: 雍俊海
//
// 简介:
// 由于多线程共享内存引发问题的例程——加减法失败。
public class J_ThreadSum extends Thread
{
public static int m_data=0;
public static int m_times=10000;
public int m_ID;
public boolean m_done;
J_ThreadSum(int id)
{
m_ID=id;
} // J_ThreadSum构造方法结束
public void run( )
{
m_done=false;
int d= ((m_ID % 2==0) ? 1 : -1);
System.out.println("运行线程: " + m_ID + "(增量为: " + d + ")");
for(int i=0; i<m_times; i++)
for(int j=0; j<m_times; j++)
m_data+=d;
m_done=true;
System.out.println("结束线程: " + m_ID);
} // 方法run结束
public static void main(String args[ ])
{
J_ThreadSum t1 = new J_ThreadSum(1);
J_ThreadSum t2 = new J_ThreadSum(2);
t1.m_done=false;
t2.m_done=false;
t1.start( );
t2.start( );
while ( !t1.m_done || !t2.m_done ) // 等待两个线程运行结束
;
System.out.println("结果: m_data=" + m_data);
} // 方法main结束
} // 类J_ThreadSum结束
线程t1和线程t2分别对同一成员域m_data进行m_times * m_times 次-1,+1,有的同学可能就会想了,加减1的次数相等,最终m_data的值应该不变,还为0呀。不不不,下面解释。
其实m_data += d; //d 为 1 或 -1
分三步
①寄存器取m_data的数据
②m_data所在数据寄存器执行加1命令
③数据从寄存器存到内存
(我们大二的在学计算机组成原理,m_data += d就是执行了这三条指令,相信我)
线程t1在执行完 m_data -1 后,结果还在寄存器,还没存到内存,紧接着执行线程t2 m_data + 1,接着结果 m_data + 1的结果放到内存,在执行线程t1把 m_data - 1的结果放到m_data所在内存。这个执行过程是可能发生的。
例如:
m_data = 5,+1,-1各执行一次。先执行线程t1, 看先执行让其结果不 为 5
m_data - 1, (结果4存到寄存器eax,接着执行另一线程,由于没有存数,m_data 还为5)
m_data = m_data + 1 (执行加1,并存数, m_data = 6,接着执行线程t1)
m_data = [eax] (这是线程t1中断的地方,好了现在m_tada = 4)
\
auther: wjsay
我相信下学期学完操作系统和汇编后更易理解。
PS:室友的七彩光机械键盘用着很cool,我怕我打字停不下来。~ha
\