成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

面試突擊:線程安全問題是如何產生的?

開發 前端
導致線程安全問題的第一大因素就是多線程搶占式執行,想象一下,如果是單線程執行,或者是多線程有序執行,那就不會出現混亂的情況了,不出現混亂的情況,自然就不會出現非線程安全的問題了。

線程安全是指某個方法或某段代碼,在多線程中能夠正確的執行,不會出現數據不一致或數據污染的情況,我們把這樣的程序稱之為線程安全的,反之則為非線程安全的。

舉個例子來說,比如銀行只有張三一個人來辦理業務,這種情況在程序中就叫做單線程執行,而單線程執行是沒有問題的,也就是線程安全的。但突然有一天來了很多人同時辦理業務,這種情況就叫做多線程執行。如果所有人都一起爭搶著辦理業務,很有可能會導致錯誤,而這種錯誤就叫非線程安全。如果每個人都能有序排隊辦理業務,且工作人員不會操作失誤,我們就把這種情況稱之為線程安全的。

問題演示

接下來我們演示一下,程序中非線程安全的示例。我們先創建一個變量 number 等于 0,然后開啟線程 1 執行 100 萬次 number++ 操作,同時再開啟線程 2 執行 100 萬次 number-- 操作,等待線程 1 和線程 2 都執行完,正確的結果 number 應該還是 0,但不加干預的多線程執行結果卻與預期的正確結果不一致,如下代碼所示:

public class ThreadSafeTest {
// 全局變量
private static int number = 0;
// 循環次數(100W)
private static final int COUNT = 1_000_000;

public static void main(String[] args) throws InterruptedException {
// 線程1:執行 100W 次 number+1 操作
Thread t1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
number++;
}
});
t1.start();

// 線程2:執行 100W 次 number-1 操作
Thread t2 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
number--;
}
});
t2.start();

// 等待線程 1 和線程 2,執行完,打印 number 最終的結果
t1.join();
t2.join();
System.out.println("number 最終結果:" + number);
}
}

以上程序的執行結果如下圖所示:

從上述執行結果可以看出,number 變量最終的結果并不是 0,和我們預期的正確結果是不相符的,這就是多線程中的線程安全問題。

產生原因

導致線程安全問題的因素有以下 5 個:

  • 多線程搶占式執行。
  • 多線程同時修改同一個變量。
  • 非原子性操作。
  • 內存可見性。
  • 指令重排序。

接下來我們分別來看這 5 個因素的具體含義。

1.多線程搶占式執行

導致線程安全問題的第一大因素就是多線程搶占式執行,想象一下,如果是單線程執行,或者是多線程有序執行,那就不會出現混亂的情況了,不出現混亂的情況,自然就不會出現非線程安全的問題了。

2.多線程同時修改同一個變量

如果是多線程同時修改不同的變量(每個線程只修改自己的變量),也是不會出現非線程安全的問題了,比如以下代碼,線程 1 修改 number1 變量,而線程 2 修改 number2 變量,最終兩個線程執行完之后的結果如下:

public class ThreadSafe {
// 全局變量
private static int number = 0;
// 循環次數(100W)
private static final int COUNT = 1_000_000;
// 線程 1 操作的變量 number1
private static int number1 = 0;
// 線程 2 操作的變量 number2
private static int number2 = 0;

public static void main(String[] args) throws InterruptedException {
// 線程1:執行 100W 次 number+1 操作
Thread t1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
number1++;
}
});
t1.start();

// 線程2:執行 100W 次 number-1 操作
Thread t2 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
number2--;
}
});
t2.start();

// 等待線程 1 和線程 2,執行完,打印 number 最終的結果
t1.join();
t2.join();
number = number1 + number2;
System.out.println("number=number1+number2 最終結果:" + number);
}
}

以上程序的執行結果如下圖所示:

從上述結果可以看出,多線程只要不是同時修改同一個變量,也不會出現線程安全問題。

3.非原子性操作

原子性操作是指操作不能再被分隔就叫原子性操作。比如人類吸氣或者是呼氣這個動作,它是一瞬間一次性完成的,你不可能先吸一半(氣),停下來玩會手機,再吸一半(氣),這種操作就是原子性操作。而非原子性操作是我現在要去睡覺,但睡覺之前要先上床,再拉被子,再躺下、再入睡等一系列的操作綜合在一起組成的,這就是非原子性操作。非原子性操作是有可以被分隔和打斷的,比如要上床之前,發現時間還在,先刷個劇、刷會手機、再玩會游戲,甚至是再吃點小燒烤等等,所以非原子性操作有很多不確定性,而這些不確定性就會造成線程安全問題問題。像 i++ 和 i-- 這種操作就是非原子的,它在 +1 或 -1 之前,先要查詢原變量的值,并不是一次性完成的,所以就會導致線程安全問題。比如以下操作流程:

操作步驟

線程1

線程2

T1

讀取到 number=1,準備執行 number-1 的操作,但還沒有執行,時間片就用完了。


T2


讀取到 number=1,并且執行 number+1 操作,將 number 修改成了 2。

T3

恢復執行,因為之前已經讀取了 number=1,所以直接執行 -1 操作,將 number 變成了 0。


以上就是一個經典的錯誤,number 原本等于 1,線程 1 進行 -1 操作,而線程 2 進行加 1,最終的結果 number 應該還等于 1 才對,但通過上面的執行,number 最終被修改成了 0,這就是非原子性導致的問題。

4.內存可見性問題

在 Java 編程中內存分為兩種類型:工作內存和主內存,而工作內存使用的是 CPU 寄存器實現的,而主內存是指電腦中的內存,我們知道 CPU 寄存器的操作速度是遠大于內存的操作速度的,它們的性能差異如下圖所示:

那這和線程安全有什么關系呢?這是因為在 Java 語言中,為了提高程序的執行速度,所以在操作變量時,會將變量從主內存中復制一份到工作內存,而主內存是所有線程共用的,工作內存是每個線程私有的,這就會導致一個線程已經把主內存中的公共變量修改了,而另一個線程不知道,依舊使用自己工作內存中的變量,這樣就導致了問題的產生,也就導致了線程安全問題。

5.指令重排序

指令重排序是指 Java 程序為了提高程序的執行速度,所以會對一下操作進行合并和優化的操作。比如說,張三要去圖書館還書,舍友又讓張三幫忙借書,那么程序的執行思維是,張三先去圖書館把自己的書還了,再去一趟圖書館幫舍友把書借回來。而指令重排序之后,把兩次執行合并了,張三帶著自己的書去圖書館把書先還了,再幫舍友把書借出來,整個流程就執行完了,這是正常情況下的指令重排序的好處。但是指令重排序也有“副作用”,而“副作用”是發生在多線程執行中的,還是以張三借書和幫舍友還書為例,如果張三是一件事做完再做另一件事是沒有問題的(也就是單線程執行是沒有問題的),但如果是多線程執行,就是兩件事由多個人混合著做,比如張三在圖書館遇到了自己的多個同學,于是就把任務分派給多個人一起執行,有人借了幾本書、有人借了還了幾本書、有人再借了幾本書、有人再借了還了幾本書,執行的很混亂沒有明確的目標,到最后悲劇就發生了,這就是在指令重排序帶來的線程安全問題。

總結

線程安全是指某個方法或某段代碼,在多線程中能夠正確的執行,不會出現數據不一致或數據污染的情況,反之則為線程安全問題。簡單來說所謂的非線程安全是指:在多線程中,程序的執行結果和預期的正確結果不一致的問題。而造成線程安全問題的因素有 5 個:多線程搶占式執行、多線程同時修改同一個變量、非原子性操作、內存可見性和指令重排序。

責任編輯:武曉燕 來源: Java面試真題解析
相關推薦

2022-04-07 07:40:40

線程安全變量

2022-01-24 07:01:20

安全多線程版本

2024-07-10 15:02:27

2013-12-06 17:12:59

2022-02-28 07:01:22

線程中斷interrupt

2022-09-07 07:05:25

跨域問題安全架構

2024-11-14 14:53:04

2022-03-23 08:51:21

線程池Java面試題

2018-12-28 09:36:06

網絡安全漏洞威脅

2022-05-11 07:41:55

死鎖運算線程

2022-03-14 07:32:06

線程池拒絕策略自定義

2022-04-13 14:43:05

JVM同步鎖Monitor 監視

2015-04-21 10:23:11

2011-03-29 10:41:51

Java線程安全

2024-09-17 17:50:28

線程線程安全代碼

2011-05-20 11:59:32

2012-11-20 10:47:16

2018-10-10 21:00:50

2011-08-08 10:29:08

2011-08-29 10:34:00

網絡安全云安全云計算
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 美女视频一区二区三区 | 91精品无人区卡一卡二卡三 | 国产精品久久精品 | 91精品国产综合久久久久久 | 韩国精品在线观看 | 国产精品视频一区二区三区不卡 | 日韩在线中文字幕 | 国产精品mv在线观看 | 久久免费看 | 91在线电影| 一级片免费视频 | 欧美精品日韩精品 | 中文字幕在线电影观看 | 欧美精品一区三区 | 精品福利视频一区二区三区 | 国产精品日日夜夜 | 最近中文字幕第一页 | 亚洲精品在线免费看 | 综合色婷婷 | 91精品国产乱码久久蜜臀 | 免费毛片网站在线观看 | 欧美精品久久久 | 精品国产一区二区三区性色av | 久久久久久国产 | 成人三级在线播放 | 人人人人干| 欧美视频1区 | 在线观看www视频 | 国产成人高清成人av片在线看 | 成人精品国产免费网站 | 97视频免费| 野狼在线社区2017入口 | 国产精品久久久久久久久久久免费看 | 国产成人福利 | 成人免费视频久久 | 国产精品久久久 | 韩国久久精品 | 岛国在线免费观看 | 国产精品不卡视频 | 国产高清在线视频 | 精品久久久久久久久久久下田 |