今天給大家帶來一個毀三觀的結論:Java代碼在執行過程中,並不一定是從上到下,每句代碼依次順序執行的。這是不是很顛覆你的認知?
計算機在執行程序的時候,並不會嚴格按照代碼來順序執行。比如第一個指令,是從內存中讀取一個數據,而第二個指令,是寄存器的某個值自增1。而如果這兩個指令是毫無衝突的,沒有任何關聯性和依賴性,誰先誰後都不影響最終結果。那麼 “機智” 的CPU就有可能調換執行的順序。比如第一個指令先通過總線去尋址、取數據,這需要很長的時間,CPU沒必要非要等到這個數據從內存中取到並賦值了之後,才執行第二個指令。CPU可以先請求總線去取數據,等數據的時候就執行第二個指令,將寄存器裏面的某個值自增1。說不定自增完了數據還沒取過來。然後假設第三條指令要對這個讀取到的數據進行操作了,那麼CPU就只能等了,等到數據讀取到了,賦值了,再進行第三條指令。因爲CPU是在是太快了,比內存的速度快兩個數量級!
所以,我們知道了計算機在執行代碼的時候,是會爲了提高效率,根據上下文來進行指令重排的。但是這個聽起來很玄乎,能不能證明呢?接下來的一個例子,就能夠通過Java語言來證明,Java代碼並不是一定按照代碼的順序,一句一句依次順序執行的:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Author: LiYang
* @Date: 2020/6/13 11:19
* @Description: 證明Java代碼執行中存在指令重排
*/
public class Disorder {
//四個獨立的類靜態變量
private static int a = 0;
private static int b = 0;
private static int x = 0;
private static int y = 0;
/**
* 證明Java代碼執行中存在指令重排的情況
* @param args
*/
public static void main(String[] args) throws InterruptedException {
//計數
int count = 0;
//線程池,固定兩個線程
ExecutorService executor = Executors.newFixedThreadPool(2);
//一直執行,直到出現指令重排
while (true) {
//所有變量全部恢復初始值
a = 0;
b = 0;
x = 0;
y = 0;
//計數加1
count ++;
//倒計時門栓,同步兩個線程
CountDownLatch latch = new CountDownLatch(2);
//第一個線程
executor.execute(new Runnable() {
@Override
public void run() {
a = 1;
y = b;
latch.countDown();
}
});
//第二個線程
executor.execute(new Runnable() {
@Override
public void run() {
b = 1;
x = a;
latch.countDown();
}
});
//阻塞以上兩個線程,等兩個線程全部執行完畢後,繼續執行下面的代碼
latch.await();
//此時,上面的兩個線程已經執行完畢。如果Java代碼在執行過程中都是
//一行一行的代碼順序執行的,那麼上面兩個線程無論是怎樣的先後執行
//順序,a=1和b=1,這兩句代碼作爲第一行代碼,總有一句是最先執行的,
//也就是說,到最後x或y肯定都不爲0。如果到最後x和y都同時爲0了,那
//麼根據反證法原理,Java代碼在執行過程中,有可能並不絕對按照代碼
//順序依次執行,當然也就發生了指令重排,Java代碼執行中存在指令重排
//的情況也就得到了證明
//如果x和y都等於0了,就輸出結果(如果Java代碼真的按照順序一句一句
//執行的話,是絕對不可能出現x和y同時爲0的)
if (x == 0 && y == 0) {
//指令重排輸出語句
String message = "第" + count + "次出現指令重排,x=" + x + ",y=" + y;
//打印,證明完畢
System.err.println(message);
//結束程序
System.exit(0);
}
}
}
}
執行上面的代碼,真的出現了x和y都爲0的情況,通過反證法,也就證明了CPU在執行程序的過程中確實存在指令重排的現象,以Java爲例,Java代碼在執行過程中,並不一定是從上到下,每句代碼依次順序執行的(可能你的輸出結果和我不一樣,每次出現指令重排需要的時間不一定,有可能很快,也有可能要等很久):
第95587次出現指令重排,x=0,y=0