从事Java编程的程序员若想有所成长,就肯定绕不过并发编程,今天就聊一聊在Java并发编程中volatile关键字。
以下是本文的目录大纲:
一. 并发编程中的三个概念
二. 为什么没有退出循环
三. 进一步认识volatile关键字
四. volatile关键字的使用场景
五. 一道面试题
一. 并发编程中的三个概念
在并发编程中,我们通常需要考虑三个问题:原子性问题,可见性问题,有序性问题。我们先看具体看一下这三个概念:
原子性
原子性是指一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
比如我们去12306上面买高铁票,进入买票的页面时,通常有选择乘车人--->选择座位--->付款--->出票这四个步骤,这些步骤在执行过程中就需要保存原子性。不能说我付了钱,但是当应该给我票时程序却暂停了,不在继续执行了。如果是这样,那恐怕就不会有人用12306了
public void buyingTickets(){
// 选择乘车人
selectPassengers();
// 选择座位
selectSeat();
// 付款
payment();
// 出票
getTickets();
}
可见性
可见性是指当多个线程访问同一个全局变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
还是以12306上买票为例,打开12306买票时,我们都会看到剩余票数,这个剩余票数在程序实现中就是一个多线程共享的全局变量,这里我把起名为remainingTickets。那这个remainingTickets的取值在多个线程中是可见的,当线程1修改了remainingTickets的值时,线程2看到的remainingTickets的值应该是线程1修改过后的值。比如当前剩余票数remainingTickets=10;有A和B同时通过12306来购买同一班高铁票(始发地和目的地相同)。假如A先购买完成,那B购买时,看到的剩余票数remainingTickets=9。否则如果一个人买到票后,另一个看到票数却未做相应的扣减,那节假日再也不怕买不到票了,高铁恐怕也要经常超载了。
有序性
有序性指的是指令重排序,在Java内存模型中,在不影响单线程情况下程序执行结果的前提下,允许编译器和处理器对指令进行重排序,从而提高执行效率。
这种重排序可以分为两种情况来判断,但是这两种情况归根结底都是最后执行的汇编指令的重排序。
第一种情况是我有多行代码,编译器对多行代码的执行指令进行了重排序,比如:
int i = 0;
boolean flag = false;
i = 1; //语句1
flag = true; //语句2
上面代码定义了一个int型变量,定义了一个boolean类型变量,然后分别对两个变量进行赋值操作。从代码顺序上看,语句1是在语句2前面的,那么JVM在真 正执行这段代码的时候并不一定保证语句1一定会在语句2前面执行,有可能会因为指令重排序导致语句2在语句1前面执行。这里无论是语句1先执行还是语句2先执行都不影响程序的执行结果。
第二种情况就是我就一行代码,但是这一行代码涉及多条执行指令。比如构造