執行緒相關的面試題經久不衰,尤其是在JAVA領域
曾經某架構師對我說,不懂執行緒的是初級,懂執行緒的是高階,半懂不懂的是中級。
可見執行緒在面試中是一個怎樣的角色了。但是,面試造火箭,入職擰螺絲是常態。我們真的有必要學執行緒嗎?
有,很有必要!
就算是擰螺絲,你拿著比別人多的薪水擰,難道不舒服嗎?
什麼是執行緒?
你就是一個執行緒
或者說,你擰螺絲的時候真像一個執行緒.
執行緒在計算機中是CPU的最小排程單位,啥意思呢?
程式要執行就會在記憶體中佔用記憶體,那麼計算機要怎麼給程式分配記憶體呢,這個就由作業系統來管理分配,作業系統要怎麼統一分配呢?
這時就產生了程式的概念。
作業系統會為每一個程式分配一塊地盤,並加上標識,記錄這是哪個程式的地盤。
地盤分完了得幹活呀。誰來幹呢?執行緒就出馬了,每個程式都會有一個執行緒在工作。
而且一個程式可以有N個執行緒存在.
但同一時間,只能執行一個執行緒的工作(單核CPU中)。
這個就是所謂的最小排程單位,也可以說是最小工作單位。
所以說,你在擰螺絲的時候就是個執行緒。而在公司裡有千千萬萬個這樣的執行緒。
假設,你接到一個需求,要擰200個螺絲,你一看文件現在還有200個整沒擰。 這時候你的同事也接到這個需求,一看文件剩200個沒擰。 這時候你們都去擰了一個,各自記錄-1,還剩199個沒擰。 但其實已經擰了2個了,這就有問題了。
用程式碼來演示一下,300個人擰200個螺絲會出現什麼情況。
package Thread;
public class MyRun implements Runnable {
public static int luosi = 200;
@Override
public void run() {
for (int i = 1;i<=100;i++) { // 100人去擰這200個螺絲
if (luosi > 0) {
try {
Thread.sleep(50); // 假設每個人耗費50毫秒去擰
} catch (InterruptedException e) {
e.printStackTrace();
}
luosi-= 1; // 擰成功了,總數減去1
System.out.println(Thread.currentThread()+"擰了螺絲,還剩:"+luosi+"個沒擰");
}
}
}
}
再來個入口函式去執行:
package Thread;
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
MyRun mr = new MyRun();
Thread mh = new Thread(mr); // 第一個地方 100個人擰
Thread mh2 = new Thread(mr); // 第二個地方
Thread mh3 = new Thread(mr); // 第三個地方
mh.start();
mh2.start();
mh3.start();
}
}
結果:
Thread[Thread-1,5,main]擰了螺絲,還剩:2個沒擰
Thread[Thread-0,5,main]擰了螺絲,還剩:2個沒擰
Thread[Thread-1,5,main]擰了螺絲,還剩:0個沒擰
Thread[Thread-2,5,main]擰了螺絲,還剩:-1個沒擰
Thread[Thread-0,5,main]擰了螺絲,還剩:0個沒擰
再來看下總共擰了多少螺絲
可以看到擰了222次螺絲。
這個就是傳說中的執行緒安全問題。
多個執行緒操作同一個資料,出現的資料紊亂現象
回想一下,在擰螺絲的時候,是否都要在擰之前檢視一下還有多少個螺絲沒擰.
因為每個人都有一份文件,各自優先更新自己的那份,沒有及時同步給其他人.
這個例子和JAVA工作模式很是相近,畫個圖讓大家理解一下:
每個執行緒都是優先操作自己的工作區,而主記憶體更新有可能不及時。
最經典的一個辦法
- 加鎖
保證同一時間只有一個人在操作,並且直接更新主記憶體資料,拿到鎖的一方也必須從主記憶體讀取最新資料進行操作.
JAVA如何解決:
- synchronized
package Thread;
public class MyRun implements Runnable {
public static int luosi = 200;
@Override
public void run() {
for (int i = 1;i<=100;i++) { // 100人去擰這200個螺絲
synchronized (MyRun.class) { // 加鎖,保證原子性,可見性操作
if (luosi > 0) {
try {
Thread.sleep(50); // 假設每個人耗費50毫秒去擰
} catch (InterruptedException e) {
e.printStackTrace();
}
luosi-= 1; // 擰成功了,總數減去1
System.out.println(Thread.currentThread()+"擰了螺絲,還剩:"+luosi+"個沒擰");
}
}
}
}
}
結果:
Thread[Thread-0,5,main]擰了螺絲,還剩:4個沒擰
Thread[Thread-0,5,main]擰了螺絲,還剩:3個沒擰
Thread[Thread-2,5,main]擰了螺絲,還剩:2個沒擰
Thread[Thread-1,5,main]擰了螺絲,還剩:1個沒擰
Thread[Thread-1,5,main]擰了螺絲,還剩:0個沒擰
擰了多少:
這次正常了,我們完美的解決了執行緒安全問題。
執行緒安全問題只會出現在多個執行緒操作同一個資料上,否則不會出現執行緒安全問題。 而一般解決這種問題的方式就是加鎖。我們回想一下,這個鎖是不是很熟悉,在MySQL中,多個事務操作同一條資料也是通過加鎖來隔離的。而這都會造成一個共同的問題,效能下降,甚至死鎖問題。
加鎖是否是解決執行緒安全問題的最優解呢?
而且我們在做業務需求時,真的有必要開啟多執行緒嗎?我覺得這200個螺絲,我一個人擰的更快!
PS:你的贊是我創作的動力!
為什麼同是9年義務教育別人就那麼優秀?
想知道請關注訂閱號:多邊形戰士(關注送 laravel,linux,nginx 等學習資料!!!)
回覆’學習’,推薦你2本書。
回覆’資源’,獲取我收集到的一些優質資源。
本作品採用《CC 協議》,轉載必須註明作者和本文連結