多線程的問題都曾經困擾過每個開發人員,今天將從全新視角來解說,希望讀者都能明白。 強烈建議去運行下文章中的示例代碼,自己體會下。 問題究竟出在哪裡?一個線程執行,固然是安全的,但是有時太慢了,怎麼辦?老祖宗告訴我們,“一方有難,八方支援”,那不就是多叫幾個線程來幫忙嘛,好辦呀,多new幾個不就行了, ...
多線程的問題都曾經困擾過每個開發人員,今天將從全新視角來解說,希望讀者都能明白。
強烈建議去運行下文章中的示例代碼,自己體會下。
問題究竟出在哪裡?
一個線程執行,固然是安全的,但是有時太慢了,怎麼辦?
老祖宗告訴我們,“一方有難,八方支援”,那不就是多叫幾個線程來幫忙嘛,好辦呀,多new幾個不就行了,又不要錢。這樣能管用嗎?繼續往下看。
俗話說,“在家靠父母,出門靠朋友”。有了朋友的幫助,就會事半功倍。是這樣的嗎?
不一定,如果朋友“不靠譜”,結果竟是在“添亂”。於是就演變為,“不怕神一樣的對手,就怕豬一樣的隊友”。可見“人多力量大”縱然是對的,但也要配合好才能成事。
人和人是朋友,那線程和線程也是“朋友”,如果多線程之間不能配合好的話,最終也會變為“豬一樣的隊友”。事實證明,這也不是一件易事。且容我慢慢道來。
開發是一門技術,管理是一門藝術。也許你正想帶著兄弟們大幹一場,可偏偏就有人要辭職。或者你付出了這麼多,但別人從來沒有感動過。為什麼會這樣呢?
因為你面對的是人。每個人都是獨立的個體,有思想,有靈魂,有情感,有三觀。能夠接受外界的“輸入”,經過“處理”後,能夠產生“輸出”。
說白了就是會自主的分析問題,並做出決定。這叫什麼呢?答案就是,主觀能動性。
擁有主觀能動性的物體(比如人),你需要和它協商著或配合著來共同完成一件事情,而不能“強迫”它去做什麼,因為這樣往往不會有好的結果。
費了這麼多口舌,就是希望把問題儘量的簡單化。終於可以回到程式了,那線程的情況是不是類似的呢?答案是肯定的。
一個線程準備好後,經過CPU的調度,就可以自主的運行了。此時它儼然成了一個獨立的個體,且具有主觀能動性。
這本是一件好事,但卻也有不好的一面,那就是你對它的“掌控”能力變弱了,頗有一種“將在外,君命有所不受”的感覺。
可能你不同意這種看法,說我可以“強迫”它停止運行,調用Thread類的stop()方法來直接把它“掐死”,不好意思,該方法已廢棄。
因為線程可能在運行一些“關鍵”代碼(比如轉賬),此刻不能被終止。Thread類還有一些其它的方法也都廢棄了,大抵原因其實都差不多。
講了這麼多,相信你已經明白了,簡單總結一下:
事情起因:線程可以獨立自主的運行,可以認為它具有主觀能動性。
造成結果:對它的掌控能力變弱了,而且又不能直接把它“幹掉”。
解決方案:凡事商量著來,互相配合著把事情完成。
作者觀點:其實就是把線程當作人來對待。
小試牛刀一下
一旦把線程當成人,就來到了人類的世界,這我們太熟悉了,所以很多問題都會變得非常簡單明瞭。一起來看看吧。
場景一,停止
“大胖,大胖,12點了,該去吃飯了,別寫了”
“好的,好的,稍等片刻,把這幾行代碼寫完就走”
要點:把停止的信號傳達給別人,別人處理完手頭的事情就自己主動停止了。
static void stopByFlag() {
ARunnable ar = new ARunnable();
new Thread(ar).start();
ar.tellToStop();
}
static class ARunnable implements Runnable {
volatile boolean stop;
void tellToStop() {
stop = true;
}
@Override
public void run() {
println("進入不可停止區域 1。。。");
doingLongTime(5);
println("退出不可停止區域 1。。。");
println("檢測標誌stop = %s", String.valueOf(stop));
if (stop) {
println("停止執行");
return;
}
println("進入不可停止區域 2。。。");
doingLongTime(5);
println("退出不可停止區域 2。。。");
}
}
解說:線程在預設的地點檢測flag,來決定是否停止。
場景二,暫停/恢復
“大胖,大胖,先別發請求了,對方伺服器快掛了”
“好的,好的,等這個執行完就不發了”
過了一會
“大胖,大胖,可以重新發請求了”
“好的,好的”
要點:把暫停的信號傳達給別人,別人處理完手頭的事情就自己主動暫停了。但是恢復是無法自主進行的,只能由操作系統來恢複線程的執行。
static void pauseByFlag() {
BRunnable br = new BRunnable();
new Thread(br).start();
br.tellToPause();
sleep(8);
br.tellToResume();
}
static class BRunnable implements Runnable {
volatile boolean pause;
void tellToPause() {
pause = true;
}
void tellToResume() {
synchronized (this) {
this.notify();
}
}
@Override
public void run() {
println("進入不可暫停區域 1。。。");
doingLongTime(5);
println("退出不可暫停區域 1。。。");
println("檢測標誌pause = %s", String.valueOf(pause));
if (pause) {
println("暫停執行");
try {
synchronized (this) {
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
println("恢復執行");
}
println("進入不可暫停區域 2。。。");
doingLongTime(5);
println("退出不可暫停區域 2。。。");
}
}
解說:還是在預設的地點檢測flag。然後就是wait/notify配合使用。
場景三,插隊
“大胖,大胖,讓我站到你前面,不想排隊了”
“好吧”
要點:別人插隊到你前面,必須等他完事後才輪到你。
static void jqByJoin() {
CRunnable cr = new CRunnable();
Thread t = new Thread(cr);
t.start();
sleep(1);
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
println("終於輪到我了");
}
static class CRunnable implements Runnable {
@Override
public void run() {
println("進入不可暫停區域 1。。。");
doingLongTime(5);
println("退出不可暫停區域 1。。。");
}
}
解說:join方法可以讓某個線程插到自己前面,等它執行完,自己才會繼續執行。
場景四,叫醒
“大胖,大胖,醒醒,醒醒,看