一.多線程的概念
線程概念
線程就是程序中單獨順序的流控制。
線程本身不能運行,它只能用於程序中。
說明:線程是程序內的順序控制流,只能使用分配給程序的資源和環境。
進程:操作系統中執行的程序
程序是靜態的概念,進程是動態的概念。
一個進程可以包含一個或多個線程。
一個進程至少要包含一個線程。
單線程
單個程序中只有一個執行路徑就是單線程。
當程序啟動運行時,就自動產生一個線程,主方法main就在這個主線程上運行。我們的程序都是由線程來執行的。
多線程
多線程指在單個程序中可以同時運行多個不同的線程執行不同的任務。
多線程編程的目的,就是“最大限度地利用CPU資源”,當某一線程的處理不需要占用CPU而只和IO等資源打交道時,讓需要占用CPU的其他線程有機會獲得CPU資源。從根本上說,這就是多線程編程的最終目的。
一個程序實現多個代碼同時交替運行就需要產生多個線程。
CPU隨機地抽出時間,讓我們的程序一會做這件事情,一會做另外的事情。
從宏觀角度來看,多個線程在同時執行(宏觀並行),但是微觀上來看,處理器的個數決定了某一個時刻可以同時運行的最大線程數,
如單核CPU某一時刻只能有一個線程在執行(微觀串行),雙核的CPU在某一個時刻,最多可以運行兩個線程,可以做到微觀並行。
二.java中實現多線程的方式
1.繼承Thread類並重寫它的run方法。之后創建這個子類的對象並調用start()方法。
2.通過定義實現Runnable接口的類進而實現run方法。這個類的對象在創建Thread的時候作為參數被傳入,然后調用start()方法。
Thread類是專門用來創建線程和對線程進行操作的類。當某個類繼承了Thread類之后,該類就叫做一個線程類。
兩種方法均需執行線程的start()方法為線程分配必須的系統資源、調度線程運行並執行線程的run()方法。
注意:start()方法是啟動線程的唯一的方法。start()方法首先為線程的執行准備好系統資源,然后再去調用run()方法。
run()方法中放入了線程的工作,即我們要這個線程去做的所有事情。缺省狀況下run()方法什么也不做。
1.通過繼承Thread類實現多線程
/** * 通過Thread實現線程 */ Thread thread1 = new Thread() { @Override public void run() { while (true) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread1:" + Thread.currentThread().getName()); } } }; thread1.start();
2.通過實現runnable接口實現多線程
/** * 通過Runnable接口實現線程 */ Thread thread2 = new Thread(new Runnable() { public void run() { while (true) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread2:" + Thread.currentThread().getName()); } } }); thread2.start();
代碼解析:
Thread類也實現了Runnable接口,因此實現了接口中的run()方法。
當生成一個線程對象時,如果沒有為其指定名字,那么線程對象的名字將使用如下形式:Thread-number,該number是自動增加的數字,並被所有的Thread對象所共享,因為它是一個static的成員變量。
當使用第一種方式(繼承Thread的方式)來生成線程對象時,我們需要重寫run()方法,因為Thread類的run()方法此時什么事情也不做。
當使用第二種方式(實現Runnable接口的方式)來生成線程對象時,我們需要實現Runnable接口的run()方法
停止線程
線程的消亡不能通過調用stop()命令,而是讓run()方法自然結束。stop()方法是不安全的,已經廢棄。
停止線程推薦的方式:設定一個標志變量,在run()方法中是一個循環,由該標志變量控制循環是繼續執行還是跳出;循環跳出,則線程結束。
三.線程的生命周期
1.創建狀態:
當用new操作符創建一個新的線程對象時,該線程處於創建狀態。
處於創建狀態的線程只是一個空的線程對象,系統不為它分配資源。
2.可運行狀態:
執行線程的start()方法將為線程分配必須的系統資源,安排其運行,並調用線程體——run()方法,這樣就使得該線程處於可運行狀態(Runnable)。
這一狀態並不是運行中狀態(Running),因為線程也許實際上並未真正運行。
3.不可運行狀態:
當發生下列事件時,處於運行狀態的線程會轉入到不可運行狀態:
調用了sleep()方法;
線程調用wait()方法等待特定條件的滿足;
線程輸入/輸出阻塞。
返回可運行狀態:
處於睡眠狀態的線程在指定的時間過去后;
如果線程在等待某一條件,另一個對象必須通過notify()或notifyAll()方法通知等待線程條件的改變;
如果線程是因為輸入輸出阻塞,等待輸入輸出完成。
sleep與wait區別:
1.對於sleep()方法,我們首先要知道該方法是屬於Thread類中的。而wait()方法,則是屬於Object類中的。
2.sleep()方法導致了程序暫停執行指定的時間,讓出cpu該其他線程,但是他的監控狀態依然保持者,當指定的時間到了又會自動恢復運行狀態。
在調用sleep()方法的過程中,線程不會釋放對象鎖。
而當調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法后本線程才進入對象鎖定池准備
notify與notifyall區別
notify()和notifyAll()都是Object對象用於通知處在等待該對象的線程的方法。 void notify(): 喚醒一個正在等待該對象的線程。 void notifyAll(): 喚醒所有正在等待該對象的線程。 兩者的最大區別在於: notifyAll使所有原來在該對象上等待被notify的線程統統退出wait的狀態,變成等待該對象上的鎖,一旦該對象被解鎖,他們就會去競爭。 notify他只是選擇一個wait狀態線程進行通知,並使它獲得該對象上的鎖,但不驚動其他同樣在等待被該對象notify的線程們,
當第一個線程運行完畢以后釋放對象上的鎖,此時如果該對象沒有再次使用notify語句,即便該對象已經空閑,其他wait狀態等待的線程由於沒有得到該對象的通知,
繼續處在wait狀態,直到這個對象發出一個notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖。
4.消亡狀態:
當線程的run()方法執行結束后,該線程自然消亡。
四.線程的優先級
1.線程的優先級及設置
線程的優先級是為了在多線程環境中便於系統對線程的調度,優先級高的線程將優先執行。
一個線程的優先級設置遵從以下原則:
線程創建時,子繼承父的優先級。
線程創建后,可通過調用setPriority()方法改變優先級。
線程的優先級是1-10之間的正整數。
1- MIN_PRIORITY
10-MAX_PRIORITY
5-NORM_PRIORITY
如果什么都沒有設置,默認值是5。
但是不能依靠線程的優先級來決定線程的執行順序。
2.線程的調度策略
線程調度器選擇優先級最高的線程運行。但是,如果發生以下情況,就會終止線程的運行:
線程體中調用了yield()方法,讓出了對CPU的占用權。
線程體中調用了sleep()方法,使線程進入睡眠狀態。
線程由於I/O操作而受阻塞。
另一個更高優先級的線程出現。
在支持時間片的系統中,該線程的時間片用完。