01-3 JMM-多线程

        由于并发程序要比串行程序复杂很多,其中一个重要原因是并发程序中数据访问的一致性和安全性将会受到严重挑战。如何保证一个线程可以看到正确的数据呢?这个问题看起来很白痴。对于串行程序来说,根本就是小菜一碟,如果你读取一个变量,这个变量的值是1,那么你读到的一定是1,就是这么简单的问题在并行程序中居然变得复杂起来。事实上,如果不加控制的任由线程胡乱并行,即使原本是1的数据,你也有可能读到2。因此,我们需要在深入了解并行机制的前提下,在定义一种规则,保证多个多线程间可以有效地、正确地协同工作。而JMM也就是为此而生的。

        JMM的关键技术点都是围绕着多线程的原子性、可见性和有序性来建立的。因此,我们首先必须了解这些概念。

1、原子性(Atomicity)

        原子性是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。

        比如,对于一个静态全局变量int i,两个线程同时对它赋值,线程A给它赋值1,线程B给他赋值为-1,那么不管这两个线程以何总方式、何种步调工作,i的值要么是1,要么是-1。线程A和线程B之间是没有干扰的。这就是原子性的一个特点,不可被中断。

        但如果我们不使用int型数据而使用long型数据,可能就没有那么幸运了。对于32位系统来说,long型数据的读写不是原子性的(因为long型数据有64位)。也就是说,如果两个线程同时对long型数据进行写入(或者读取),则对线程之间的结果是有干扰的。

2、可见性(Visibility)

        可见性是指当一个线程修改了某一个共享变量的值时,其他线程是否能够立即知道这个修改。显然,对于串行程序来说,可见性问题是不存在的。因为你在任何一个操作步骤中修改了某个变量,在后续的步骤中读取这个变量的值时,读取的一定是修改后的新值。

        但是这个问题存在于并行程序中。如果一个线程修改了某一个全局变量,那么其他线程未必可以马上知道这个改动。如果在CPU1和CPU2上各运行了一个线程,它们共享变量t,由于编译器优化或者硬件优化的缘故,在CPU1上的线程将变量t进行了优化,将其缓存在cache中或者寄存器里。在这种情况下,如果在CPU2上的某个线程修改了变量t的实际值,那么CPU1上的线程可能无法意识到这个改动,依然会读取cache中或者寄存器里的数据。因此,就产生了可见性问题。

        可见性问题是一个综合性问题。除上面提到的缓存优化或者硬件优化(有些内存读写可能不会立即触发,而会先进入一个硬件队列等待)会导致可见性问题以外,指令重排及编译器的优化,也有可能导致一个线程的修改不会立即被其他线程察觉。

3、有序性(Ordering)

        有序性问题可能是三个问题中最难理解的了。对于一个线程的执行代码而言,我们总是习惯性地认为代码是从前往后依次执行的。这么理解也不能说完全错误,因为就一个线程而言,确实会表现成这样。但是,在并发时,程序的执行可能就会出现乱序。给人的直观感觉就是:写在前面的代码,会在后面执行。听起来有点不可思议,是吗?有序性问题的原因是程序在执行时,有可能进行指令重排,重排后的执行与原指令的顺序未必一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值