您的位置:首页 > 编程语言 > Java开发

《Java高并发程序设计》学习 --1.4Java内存模型

2017-03-15 21:20 225 查看
1)原子性
原子性是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
比如,对于一个静态全局变量int i,两个线程同时对它赋值,线程A给它赋值1,线程B给它赋值为-1。那么不管这2个线程以何种方式、何种步调工作,i的值要么是1,要么是-1、线程A和线程B之间是没有干扰的。这就是原子性的一个特点,不可被中断。
但是如果不使用int而使用long型的话,可能就没那么幸运了。对于32位系统来说,long型数据的读写不是原子性的(因为long有64位)。也就是说,如果两个线程同时对long进行写入的话(或者读取),对线程之间的结果是有干扰的。

public class E1 {  
public static long t=0;  
public static class ChangT implements Runnable {  
private long to;  
public ChangT(long to) {  
this.to = to;  
}  
@Override public void run() {  
while (true) {  
E1.t = to;  
Thread.yield();  
}  
}  
}  
public static class ReadT implements Runnable {  
@Override public void run() {  
while (true){  
long tmp = E1.t;  
if (tmp != 111L && tmp != -999L && tmp != 333L && tmp != -444L)  
System.out.println(tmp);  
Thread.yield();  
}  
}  
}  
public static void main(String[] a) {  
new Thread(new ChangT(111L)).start();  
new Thread(new ChangT(-999L)).start();  
new Thread(new ChangT(333L)).start();  
new Thread(new ChangT(-444L)).start();  
new Thread(new ReadT()).start();  
}
}

上述代码有4个线程对long型数据t进行赋值,分别对t赋值为111、-999、333、444。然后,有一个读取线程,读取这个t的值。一般来说,t的值总是这4个数值中的一个。但在32位的Java虚拟机中,未必总是这样的。
如果读取线程ReadT总是读到合理的数据,那么这个程序应该没有任何输出。但是,实际上,这个程序一旦运行,就会大量输出一下信息:
...-42949669634294966852-4294966963...
可以看到,读取线程居然读到了两个似乎根本不可能存在的数据。其中的原因也就是因为32位系统中long型数据的读和写都不是原子性的,多线程之间相互干扰了;
如果给出这几个数值的2进制表示,就会有更清晰的认识:
-999 = 1111111111111111111111111111111111111111111111111111110000011001
-444 = 1111111111111111111111111111111111111111111111111111111001000100
111 = 0000000000000000000000000000000000000000000000000000000001101111
333 = 0000000000000000000000000000000000000000000000000000000101001101
4294966852 = 0000000000000000000000000000000011111111111111111111111001000100
-4294967185 = 1111111111111111111111111111111100000000000000000000000001101111
上面显示了这几个相关数字的补码形式,也就是在计算机内的真实存储内容。不难发现,这个奇怪的4294966852,其实是111或者333的前32位,与-444的后32位夹杂后的数字。而-4294967185只是-999或者-444的前32位与111夹杂后的数字。换句话说,由于并行的关系,数字被写乱了,或者读的时候,读串位了。
2)可见性
可见性是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。对于串行程序来说这个问题是不存在的。但这个问题在并行程序中就很有可能出现。如果一个线程修改了某一个全局变量。其他线程未必可以马上知道这个修改。如果CPU1和CPU2上各运行了一个线程,它们共享变量t。由于编译器优化或者硬件优化缘故。在CPU1上的线程将变量t进行了优化,将其缓存在cache中或者寄存器里。这种情况下如果CPU2上的某个线程修改了t的实际值,那么CPU1上的线程可能就无法意识到这个改动,依旧会读取cache或者寄存器中的旧值。因此就产生了可见性的问题。可见性问题在并行程序中也是需要重点关注的问题之一。 
3)有序性
有序性的问题的原因是因为程序在执行的时候,可能发生指令重排,重排后的指令和原指令的顺序未必一致。

注:本篇博客内容摘自《Java高并发程序设计》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: