Java多线程-CSDN博客

62 阅读2分钟

从单条指令级上分析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

\