一、多線程之間的通信。
就是多個線程在操作同一份數據, 但是操作的方法不同。
如: 對于同一個存儲塊,其中有兩個存儲位:name ? sex, 現有兩個線程,一個向其中存放數據,一個打印其中的數據。
為了解決上述問題中的安全問題(在存放線程進行存放操作的時候, 打印線程不能對共有數據進行操作),所以應當對兩個線程
? ? ? ?操作共有數據的代碼部分進行同步(使用synchronized(),來進行同步, 注意 :使用同一個對象作為同步鎖。
二、等待喚醒機制。
在上述案例實現過后運行,會發現:打印結果順序并不是按照存放順序進行的, 所以在此處就引入了等待喚醒機制。
解釋:就是在一個線程進行了規定操作后,就進入等待狀態(wait()), 等待其他線程執行完他們的指定代碼過后 再將其喚醒(notify())。
? ? 在有多個線程進行等待時, 如果需要,可以使用 notifyAll()來喚醒所有的等待線程。
所使用的方法(關鍵字):
對象名.wait(); 在其他線程調用此對象的 notify() 方法或 notifyAll() 方法前,導致當前線程等待。
對象名.notify(); 喚醒在此對象監視器上等待的單個線程。
對象名.notifyAll();喚醒在此對象監視器上等待的所有線程。
注意:以上方法(關鍵字),都必須使用在同步中,因為其需要對持有該對象監視器所有權(鎖)的線程進行操作。
所以必須使用在同步中,因為只有同步中的線程才具有該對象監視器的所有權(鎖),
wait()和sleep()一樣,都會拋出 InterruptedException 異常。
而且,以上方法都定義在類:Object中
是因為,這些方法在操作同步中的線程的時候,都必須標示其所操作線程所持有的鎖(被該鎖的對象調用),
而只有同一個對象監視器下(同一個鎖上的)的被等待線程,可以被持有該鎖的線程喚醒,(無法喚醒不同鎖上的線程)
即: 等待和喚醒的必須是同一個對象的監視器下(同一個鎖上)的線程。
而鎖可以是任意已近確定的對象, 能被任意對象調用的方法應當定義在 Object類中。
監視器(鎖): 同一個對象的監視器下(同一個鎖上)的線程,一次只能執行一個:就是擁有監視器所有權(持有鎖)的那一個線程。
三、接口Lock和 接口Condition
注:在使用Lock 與Condition 時,必須引入其工具包:import java.util.concurrent.locks.*
在jdk1.5以上,為了解決多個線程同時在對一份共享數據進行不同操作時出現的安全問題(要么陷入所有線程同時等待的狀態、
要么每次喚醒所有等待線程的問題),
其為我們提供了新的 鎖工具: Lock來替換 synchronized , 由 Lock對象所創建的綁定于該Lock對象的 condition對象
??替換了object類中的wait,notify操作。
1、Lock接口, 已知實現該接口的類: ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
1)、 ReentrantLock(大體功能與 synchronized相當,不過可以綁定多個condition)
構造方法:ReentrantLock() 創建一個 ReentrantLock 的實例。
ReentrantLock(boolean fair) 創建一個具有給定公平策略的 ReentrantLock
常用方法: void lock() :獲取一個鎖。
?void unlock() :釋放該鎖。
?Condition newCondition() 返回用來與此 Lock 實例一起使用的 Condition 實例。
2、Contition接口, 已知實現類:AbstractQueuedLongSynchronizer.ConditionObject, AbstractQueuedSynchronizer.ConditionObject(暫時不學習)
常用方法:void await() 造成當前線程在接到信號或被中斷之前一直處于等待狀態。
void signal() 喚醒一個等待線程。
void signalAll() 喚醒所有等待線程。
常見代碼示例:
class BoundedBuffer {
final Lock lock = new ReentrantLock(); //創建一個Lock的子類對象 lock
final Condition notFull = lock.newCondition(); //調用lock的newCondition方法,創建一個condition子類對象 notFull
final Condition notEmpty = lock.newCondition(); //調用lock的newCondition方法,創建一個condition子類對象 notEmpty
public void put(Object x) throws InterruptedException { //
lock.lock(); //獲取鎖
try {
while (判斷語句)
notFull.await(); //判斷成功,線程等待于notFull下。
操作代碼
notEmpty.signal(); //喚醒notEmpty下的等待線程。
} finally { //保證其后語句執行。
lock.unlock(); //釋放鎖。
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while ()
notEmpty.await();
操作代碼
notFull.signal();
} finally {
lock.unlock();
}
}
}
四、停止線程
注:在舊版本的jdk中存在stop方法,但是在新版本中。此方法被過時。
線程停止的原理: 當 run方法中的代碼執行完畢過后,就自動停止該線程。
1、當線程中是循環代碼的時候, 只要控制住循環結束,就能夠結束該線程。
特殊情況:當線程中有wait()語句或者await()等語句時,會使得線程處于凍結狀態, 讓控制循環結束的代碼或者標記無法執行或讀取,
那么線程就不會結束。
當所有線程都陷入凍結,沒有指定方法解除凍結時,就需要我們強制清除凍結狀態,這樣就可以操作標記使循環結束。
Thread類中提供了這一方法: Interrupt()方法,(等待型語句本身會有一個InterrptedException異常的判斷,
只要被Interrupt方法打斷凍結,就會拋出這一異常, 我們就可以在異常處理語句中建立循環結束的標記)。
五、守護線程
Thread類中有一個方法,調用該方法并傳入 true ,能將該線程定義為 守護線程(后臺線程),
該方法是: void setDaemon(boolean on) 將該線程標記為守護線程或用戶線程。(調用時應當傳入true)。
注意: 1、將該線程標記為守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。
(就是說,后臺線程依賴于前臺線程)
?2、該方法必須在啟動線程前調用。
示例: Thread t = new Thread();
t.setDaemon(true);
t.start();
此時的線程 t 為守護線程。
六、Join方法。
vpi解釋:void join() 等待該線程終止。
在線程A執行的時候,碰到了B線程.join方法時,A線程就會等待,等B線程執行完,才繼續執行。
(當在調用join方法時傳入參數: long millis 時,表示 A線程等待B線程執行時間最長為millis毫秒。)
join可以用來臨時加入線程執行。