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

线程方面的小问题
public class SecondThread implements Runnable{
    int i = 0;
    public void run() {
        for(i = 0;i < 50;i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
    
    public static void main(String[] args) {
        for(int i = 0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            if(i == 20){
                Runnable target = new SecondThread();
                new Thread(target, "task1").start();
                new Thread(target, "task2").start();
            }
        }
    }
}

执行了上面的代码,执行结果又这么一块
task1:41
task2:40
task2:42
就对这进行了分析,分析的结果如下
我的问题来了,既然target对象在内存的引用只有一份,在task1线程执行了i++后对内存中的i值就变成了41了,但是在轮到task2线程执行输出的时候值应该就是41了,不改是40.

------解决方案--------------------
由于楼主你没有给这两线程同步,导致线程跑起来就比较随意,啥情况都有可能发生,
你说的就是这种情况:
本来task2将要执行打印task2 :40,结果由于System.out.println(Thread.currentThread().getName() + ":" + i);这句话调用还没结束(执行到一半),线程时间片被task1 抢占了(或者由于到时间了要放弃CPU),但是由于语句没执行完,打印没有输出,i的值是40,线程状态被保存,那么这个值也就被保存了(记住,是40)
好,继续走,轮到task1执行了,执行++i,值增大了1,那么接着打印了task1:41。
完了时间片到了,task2执行,取出上次保存的线程状态,发现i还是40,执行完打印语句(task2:40),又一次循环,这个时候由于task1已经将i的值编程41了,那么task2经过++i后,值变为42,输出打印task2:42


——————这里面最重要的一点就是System.out.println(Thread.currentThread().getName() + ":" + i);这条语句没有执行完,但是i的值作为线程状态被保存起来了,因此下次取到的是个旧值。
------解决方案--------------------
我知道楼主的困惑,你觉得两个线程都共享这一个变量,那这个变量的值不应该顺序是这样。

这个时候你要明白,每个线程都有一个线程栈的,为了效率,采用了缓存策略,会将变量拷贝一份到自己的线程栈帧中,这样就没法保证第一时间获取到的是最新更新,或者更新了最新值了。

所以这时候如果使用,volatile关键字,就可以避免这种情况发生。

如果不明白,欢迎继续提问。

可以去看看《java并发编程指南》,就会很清楚了。