文章目錄
線程
進程中的單個順序控制流,是一條執行路徑
單線程:一個進程如果只有一條執行路徑,則稱爲單線程程序
多線程:一個進程如果有多條執行路徑,則稱爲多線程程序
多線程的實現方式
- 繼承Thread類,並重寫run()方法
- 實現Runnable接口,並重寫run()方法
run()方法:用來封裝被線程執行的代碼
start()方法:啓動線程;然後由JVM調用此線程的run()方法
相比繼承Thread類,實現Runnable接口的好處:
- 避免了Java單繼承的侷限性
- 適合多個相同程序的代碼去處理同一個資源的情況,把線程和程序的代碼、數據有效分離,較好的體現了面向對象的設計思想
設置和獲取線程名稱
Thread類中設置和獲取線程名稱的方法:
-
void setName(String name):將此線程的名稱更改爲等於參數name
-
String getName():返回此線程的名稱
-
通過構造方法也可以設置線程名稱
// 這是Thread類中的一個構造方法,傳遞的參數name就是線程名 // 需要通過繼承Thread的類中的構造方法裏調用super(name) public Thread(String name) { init(null, null, name, 0); }
如何獲取main()方法所在的線程名稱?
- public static Thread currentThread():返回對當前正在執行的線程對象的引用
線程調度(線程優先級)
線程有兩種調度模型
- 分時調度模型:所有線程輪流使用CPU的使用權,平均分配每個線程佔用CPU的時間片
- 搶佔式調度模型:優先讓優先級高的線程使用CPU,如果線程的優先級相同,那麼會隨機選擇一個,獲取的CPU時間片相對多一些
Java使用的是搶佔式調度模型
假如計算機只有一個CPU,那麼CPU在某一時刻只能執行一條指令,線程只有得到CPU時間片,也就是使用權,纔可以執行指令。所以說多線程程序的執行是有隨機性,因爲誰搶到CPU的使用權是不一定的
Thread類中設置和獲取線程優先級的方法:
- public final int getPriority():返回此線程的優先級
- public final void setPriority(int newPriority):更改此線程的優先級
線程默認優先級爲:5;線程優先級的範圍是:1-10
Thread類中有三個靜態變量分別代表了線程優先級的最大值、最小值和默認值:
// 線程優先級的範圍
System.out.println("這是優先級最大值:" + Thread.MAX_PRIORITY); // 10
System.out.println("這是優先級最小值:" + Thread.MIN_PRIORITY); // 1
System.out.println("這是優先級默認值:" + Thread.NORM_PRIORITY); // 5
線程優先級高僅僅表示線程獲取的CPU時間片的機率高,但是要在次數比較多,或者多次運行的時候才能看到你想要的效果
線程控制
Thread類中控制線程的幾個方法:
-
static void sleep(long millis):使當前正在執行的線程停留(暫停執行)指定的毫秒數**(注:1秒=1000毫秒)**
-
void join():等待這個線程死亡
t.join()方法只會使主線程進入等待池並等待t線程執行完畢後纔會被喚醒。並不影響同一時刻處在運行狀態的其他線程
// 啓動線程 mt1.start(); mt2.start(); // mt1調用join()方法,等待mt1該線程死亡纔會繼續執行其他代碼 // 但不影響同一時刻處在運行狀態的其他線程(mt2還在執行) mt1.join(); // 這時mt1線程死亡,開始執行下面的代碼 mt3.start(); System.out.println("---");
-
void setDaemon(boolean on):將此線程標記爲守護線程,當運行的線程都是守護線程時,Java虛擬機將退出
線程安全
線程同步保證了多線程數據安全
同步代碼塊
- 格式:
synchronized(任意對象){
多條語句操作共享數據的代碼
}
- synchronized(任意對象):就相當於給代碼加鎖了,任意對象就可以看成是一把鎖
同步代碼塊的好處和弊端
- 好處:解決了多線程的數據安全問題
- 弊端:當線程很多時,因爲每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率
同步方法
同步方法
就是把synchronized關鍵字加到方法上
-
格式:
修飾符 synchronized 返回值類型 方法名(方法參數){ }
同步方法的鎖對象
- this
同步靜態方法
就是把synchronized關鍵字加到靜態方法上
-
格式:
修飾符 static synchronized 返回值類型 方法名 (方法參數){ }
同步靜態方法的鎖對象
- 類名.class
線程安全的類
StringBuffer
- 線程安全,可變的字符序列
- 從版本JDK 5開始,被StringBuilder 替代。通常應該使用StringBuilder類,因爲它支持所有相同的操作,但它更快,因爲它不執行同步
Vector
- 從Java 2平臺v1.2開始,該類改進了List接口,使其成爲Java Collections Framework的成員。與新的集合實現不同,Vector被同步。 如果不需要線程安全的實現,建議使用ArrayList代替Vector
Hashtable
- 該類實現了一個哈希表,它將鍵映射到值。任何非null對象都可以用作鍵或者值
- 從Java 2平臺v1.2開始,該類進行了改進,實現了Map接口,使其成爲Java Collections Framework的成員。與新的集合實現不同,Hashtable被同步。 如果不需要線程安全的實現, 建議使用HashMap代替Hashtable
Lock鎖
雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們並沒有直接看到在哪裏加上了鎖,在哪裏釋放了鎖,爲了更清晰的表達如何加鎖和釋放鎖,JDK5以後提供了一個新的鎖對象Lock
Lock實現提供比使用synchronized方法和語句可以獲得更廣泛的鎖定操作
Lock中提供了獲得鎖和釋放鎖的方法
- void lock():獲得鎖
- void unlock():釋放鎖
Lock是接口不能直接實例化,這裏採用它的實現類ReentrantLock來實例化
ReentrantLock的構造方法
- ReentrantLock():創建一個ReentrantLock的實例
注意事項
注意:必須在try裏調用lock()方法!
原因:如果程序出問題,釋放鎖的操作可能不會被執行。
格式:
// 這樣就算程序出錯,釋放鎖的操作依然會執行
try{
lock.lock();
...
}finally{ lock.unlock();
}