定义
在多线程的程序中,变量不再和单线程一样那么可靠,可以简单地的设置和读取变量的值。这是因为,Java是高级语言,既便是最简单的 i++;
在实际执行的时候,都会因为翻译成底层的机器语言,而变成不止一条语句,而在CPU最终执行的时候,情况还会变得更加复杂。所以,在多线程执行的时候,往往会出现很多意想不到的结果。请看下面的代码:
可见性指的是一个变量对所有的CPU都可见。从物理结构来看,CPU内的每个核心都有自己才可见的缓存空间。为了加快数据操作,系统会将内存中的变量复制一个副本载至核心的私有缓冲上进行处理。这样一来,如果共享内存的数据发生了,副本也必需进行更新,否则则会发生错误。所以可见性,指的就是当一个CPU修改了自己缓冲区的变量数据后,必需通知其他所有的用到此变量的核心。否则就会发生不可见的错误。
案例
在以下示例中,变量 done 同时会被两个线程访问。
class Test extends Thread{
public boolean done = false;
@Override
public void run() {
done = false;
try {
Thread.sleep(100);
} catch (Exception e) { }
done = true;
System.out.printf("done = " + done);
}
public static void main(String[] args) throws Exception {
Test t = new Test ();
t.start();
System.out.printf("等待中……");
while (!t.done);
System.out.printf("完成。");
}
}
在 Test 类中,我们定义了一个全局的 boolean 型变量,在 run() 函数中,先设置其为false,然后当所有操作完成时(这里用sleep(100) 来模拟),再设置为 true。我们用这个变量来表示run()是否已经执行完成。因此,在main函数中, 我们使用了while (!t.done) ;
来进行循环等待,直接t.done为true时,才会结束循环,执行下面的打印语句。我们执行了程序,会收到以下结果:
等待中
done = true
我们发现,程序运行后,在控制台中在输出了 done = true
后,而程序仍然处于运行状态。这表明,在主程序的t.done
的状态并没有因此而改变,其值仍然为 false
,所以程序卡在这里,无法跳出循环。
在主程序中,定义了一个Test类的对象并启动。然后根据 t.done 判断程序是否结束。最后输出count的值。但是奇怪的是,程序不会输出任何内容,会一直处于“假死”状态。
解决方法一
为字段 done 添加 volatile 关键字,即
public volatile boolean done = false;
解决方法二
在 main 函数的 while 循环中添加 sleep,即:
while (!t.done) {
Thread.sleep(1);
}
结论
当一个变量被多个线程访问时,一定要考虑同步的问题。除了以上两种解决办法,Java还提供了更多方法或类。在后的文章中,我们会慢慢看到。