首先看一段程序:
程序解读:1.我们创建了一个AutomicTest类里面有一个public的将value++的方法。
2.我们在main主线程里面新建了两个线程来各做一千次循环。
程序结果预测:两个线程,每个线程做1000次value的自增,我们是不是可以设想答案就是2000呢?
那答案到底是什么呢?我们运行下看看:
运行的结果出乎我们的意料,很大程度的少于2000,那这是怎么回事呢?
我们先看下这个图,大家都可以明白,我们主程序里面的vaule是存储在主内存中,当我们启动两个线程去对value自增的时候,可以想象下我们是开辟了两个工作内存,将主内存的值读取出来写入我们的每个线程自己的工作内存,每次运行的每个线程都从自己的工作内存中读取数据,在当前线程进行自增在写入主内存中,就可以出现写覆盖问题。那我们是不是想到了一个关键字volatile,可以让我们实时读取到工作内存的数据都是最新的,那么是不是就不会出现这样的写覆盖的问题呢?
来,我们尝试下,揭开看下谜底:
我们可以看到我们在变量value前面加了volatile,但是好像不起任何作用,出现的结果还是没有达到我们需要的结果,那到底是怎么回事呢?
我们看下value++是怎么加的,这里可以分为三步:1。首先读取value的值2.value的值做++3.将value++的值在复制给到value这三步,这三步我个人觉得可以细化成每一个小的步骤,如果现在线程1执行了1和2两步,将value自增为1了,这时候线程2也进行了相应额操作,不过线程2比较强悍,直接把三步执行完毕了,将自己的最新的值写会了主内存,这时候线程1继续走,也会把值写入主内存,就又出现了我们所说的写覆盖了,所以这时候的结果还不是我们需要的2000,那我们应该怎么做,才能得到我们需要的结果呢?这里我们也明白了volatile这个关键字为什么不能阻止写覆盖呢?其实volatile这个关键字,只是保证了可见性,但是没有保证原子性,那什么又是原子性呢?
原子性:简单点说就是一系列的操作,要不都完成了,要不都不完成,即使某个任务已经完成了99%,但是最后的1%没有都是不可以的。
那我们是不是可以将这个程序改下呢?
首先我们先来认识一个名词:CAS(Compare and Swap)比较并交换 ,那这个比较并交换是怎么个意思呢?
首先他需要操作两个数,一个是自己读取的数,一个是自己读取过来改变的数,就如上面value,线程1读取了value的值为0 写入自己的工作内存,然后在进行value++的操作,将值改变为了1,这时候在写回主内存的时候,需要先看下是否主内存的value的值是否还是value=0,如果是则将自己改变的自增的value的值写入主内存,如果不是则将主内存的值读取最新的到自己的工作内存,再做一次value++,依次下去操作。这里就和我们所说的svn和git的版本控制很像了,你辛苦了一天写了几百行代码,现在下班了需要提交了,你肯定首先从仓库拉下,保证你的本地的代码是最新的才能去提交你写的代码。当然㘝你想覆盖别人的那也没办法,就是可能会被拉出午门问斩。囧囧囧
那我们现在是不是可以改造下呢?不多说,直接上代码
这里涉及到了一个底层的类Unsafe,我们后面再叙。