JAVA基礎整理-100.Java 多線編程
Java線程的概念:什麽是線程?
Windows 系統是多任務操作系統,它以進程為單位。
系統可以分配給每個進程一段有限的執行 CPU 的時間(也稱為 CPU 時間片
單任務的特點就是排隊執行,也就是同步,就像在 cmd 中輸入一條命令後,必須等待這條命令執行完才可以執行下一條命令一樣。這就是單任務環境的缺點,即 CPU 利用率大幅降低。
圖2 單線程和多線程執行模式 線程可以理解成是在進程中獨立運行的子任務
Java多線程的實現方式
實現多線程編程的方式主要有兩種:一種是繼承 Thread 類,另一種是實現 Runnable 接口。
繼承 Thread 類
使用繼承 Thread 類的方式實現多線程,最大的局限就是不支持多繼承
在使用多線程技術時,代碼的運行結果與代碼執行順序或調用順序是無關的。
除了異步調用之外,同步執行線程 start() 方法的順序不代表線程啟動的順序。
實現 Runnable 接口
如果要創建的線程類已經有一個父類,這時就不能再繼承 Thread 類
提示:從 JDK 的 API 中可以發現,實質上 Thread 類實現了 Runnable 接口,其中的 run() 方法正是對 Runnable 接口中 run() 方法的具體實現。
圖1 使用Runnable接口啟動線程流程
要啟動一個新的線程,不是直接調用 Thread 子類對象的 run() 方法,而是調用 Thread 子類的 start() 方法
兩種方法的比較
1. 繼承 Thread 類的優缺點
- Thread.currenThread().sleep();
而是可以簡單地使用 Threadsleep() 方法,繼承 Thread 類的方式使代碼變得簡單易讀。
2. 實現 Runnable 接口的優缺點
Thread 類是一個虛擬處理機嚴格的封裝,因此只有當處理機模型修改或擴展時,才應該繼承該類。由於 Java 技術只允許單一繼承,因此如果已經繼承了 Thread 類,就不能再繼承其他任何類,這會使用戶只能采用實現 Runnable 接口的方式創建線程。
Java線程的生命周期及線程的幾種狀態
線程也具有生命周期,主要包括 7 種狀態,分別是出生狀態、就緒狀態、運行狀態、等待狀態、休眠狀態、阻塞狀態和死亡狀態,
下面對線程生命周期中的 7 種狀態做說明。
- 出生狀態:用戶在創建線程時所處的狀態,在用戶使用該線程實例調用 start() 方法之前,線程都處於出生狀態。
- 就緒狀態:也稱可執行狀態,當用戶調用 start() 方法之後,線程處於就緒狀態。
- 運行狀態:當線程得到系統資源後進入運行狀態。
- 等待狀態:當處於運行狀態下的線程調用 Thread 類的 wait() 方法時,該線程就會進入等待狀態。進入等待狀態的線程必須調用 Thread 類的 notify() 方法才能被喚醒。notifyAll() 方法是將所有處於等待狀態下的線程喚醒。
- 休眠狀態:當線程調用 Thread 類中的 sleep() 方法時,則會進入休眠狀態。
- 阻塞狀態:如果一個線程在運行狀態下發出輸入/輸出請求,該線程將進入阻塞狀態,在其等待輸入/輸出結束時,線程進入就緒狀態。對阻塞的線程來說,即使系統資源關閉,線程依然不能回到運行狀態。
- 死亡狀態:當線程的 run() 方法執行完畢,線程進入死亡狀態。
提示:一旦線程進入可執行狀態,它會在就緒狀態與運行狀態下輾轉,同時也可能進入等待狀態、休眠狀態、阻塞狀態或死亡狀態。
Java多線程之間訪問實例變量
當一個線程想要執行同步方法裏面的代碼時,線程首先嘗試去拿這把鎖,如果能夠拿到鎖,那麽這個線程就可以執行 synchronize 裏面的代碼。如果不能拿到鎖,那麽這個線程就會不斷地嘗試拿鎖,直到能夠拿到為止,而且有多個線程同時去爭搶這把鎖。
Java非線程安全問題的解決方法
非線程安全主要是指多個線程對同一個對象中的同一個實例變量進行操作時會出現值被更改、值不同步的情況,進而影響程序的執行流程。
使用 synchronized 關鍵字修飾 doPost() 方法,即不允許多個線程同時修改 doPost() 方法中的變量。
Java多線程的同步機制:synchronized
所謂同步機制,指的是兩個線程同時作用在一個對象上,應該保持對象數據的統一性和整體性。Java 提供 synchronized 關鍵字,為防止資源沖突提供了內置支持。共享資源一般是文件、輸入/輸出端口或打印機。
synchronized 不僅可以用到同步方法,也可以用到同步塊。對於同步塊,synchronized 獲取的是參數中的對象鎖。
必須註意的是,Obj 對象的作用範圍不同,控制情況也不盡相同
Java curentThread()方法的作用
從結果可以發現,雖然 this.getName() 方法返回的都是 Thread-0,但是在構造方法中使用的即是 main 線程,而在 run() 方法中使用的是 A 線程。
Java isAlive()方法的作用
isAlive() 方法的作用是判斷當前的線程是否處於活動狀態。
Java sleep方法的作用(sleep())
sleep() 方法的作用是在指定的毫秒數內讓當前“正在執行的線程”休眠(暫停執行)
Java getId()方法的作用
getId() 方法的作用非常簡單,就是取得正在運行線程的唯一標識
Java yieId()方法如何使用
yieId() 方法的作用是放棄當前的 CPU 資源,將它讓給其他的任務去占用 CPU 執行時間。
Java停止(終止)線程詳解版
停止一個線程意味著在線程處理完任務之前停掉正在做的操作,也就是放棄當前的操作。
在 Java 中有以下 3 種方法可以終止正在運行的線程:
- 使用退出標識,使線程正常退出,也就是當 run() 方法完成後線程終止。
- 使用 stop() 方法強行終止線程,但是不推薦使用這個方法,因為 stop() 和 suspend() 及 resume() 一樣,都是作廢過期的方法,使用它們可能產生不可預料的結果。
- 使用 interrupt() 方法中斷線程。
停止不了的線程
interrupt() 方法的作用是用來停止線程,但 intermpt() 方法的使用效果並不像循環結構中 break 語句那樣,可以馬上停止循環。
判斷線程是不是停止狀態
this.interrupted()
:測試當前線程是否已經中斷。this.islnterrupted()
:測試線程是否已經中斷。
intermpted() 方法具有清除狀態的功能,所以第二次調用 interrupted() 方法返回的值是 false。
異常法停止線程
就可在線程中用 for 語句來判斷線程是否為停止狀態,如果是停止狀態,則後面的代碼不再運行。
在休眠中停止
從運行結果來看,如果在休眠狀態下停止某一線程則會拋出進入 InterruptedException 異常,所以會進入 catch 語句塊清除停止狀態值,使之變成 false。
強制停止線程
調用 stop() 方法可以在任意情況下強制停止一個線程。
註意:調用 stop() 方法時會拋出 java.lang.ThreadDeath 異常,但在通常情況下,此異常不需要顯式地捕捉。
釋放鎖的不良後果
使用 stop() 釋放鎖將會給數據造成不一致性的結果。
使用 return 停止線程
除了上面介紹的方法外,還可以將 intermpt() 方法與 return 結合使用來實現停止線程的效果。
Java暫停/掛起線程(suspend())和恢復線程
(resume())
在 Java 多線程中,可以使用 suspend() 方法暫停線程,使用 resume() 方法恢復線程的執行。
suspend() 與 resume() 方法
從輸出結果的時間來看,調用 suspend() 方法確實可以暫停線程,而在調用 resume() 方法後線程恢復運行狀態。
獨占問題
在使用 suspend() 方法與 resume() 方法時,如果使用不當極易造成公共的同步對象被獨占,從而使得其他線程無法訪問公共同步對象。
不同步問題
在使用 suspend() 方法與 resume() 方法時也容易出現因為線程的暫停而導致數據不同步的情況。
Java線程的優先級和執行順序
優先級概述
在 Java 語言中,線程的優先級範圍是 1~10,值必須在 1~10,否則會出現異常;優先級的默認值為 5。優先級較高的線程會被優先執行,當執行完畢,才會輪到優先級較低的線程執行。如果優先級相同,那麽就采用輪流執行的方式。
可以使用 Thread 類中的 setPriority() 方法來設置線程的優先級。語法如下:
public final void setPriority(int newPriority)
如果要獲取當前線程的優先級,可以直接調用 getPriority() 方法。語法如下:
public final int getPriority();
使用優先級
JAVA基礎整理-100.Java 多線編程