Java多線程
Java中,可運行的程序都是有一個或多個進程組成。進程則是由多個線程組成的。
最簡單的一個進程,會包括mian線程以及GC線程。
爲什麼要使用同步鎖?
什麼時候使用同步鎖呢?
Brian同步規則:如果你正在寫一個變量,它可能接下來將被另一個線程讀取,或者正在讀取一個上一次已經被另一個線程寫過的變量,那麼你必須使用同步,並且,讀寫線程都必須用相同的監視器鎖同步。
如何使用同步鎖呢?
synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
- synchronized 方法:
public synchronized void countNum(int n);
特定對象所有synchronized方法共享同一個鎖,這種機制確保了同一時刻對於每一個類實例,其所有聲明爲 synchronized 的成員函數中至多隻有一個處於可執行狀態(因爲至多隻有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問衝突(只要所有可能訪問類成員變量的方法均被聲明爲 synchronized)。
不光如此,靜態方法也可以聲明爲 synchronized ,以控制其對類的靜態成員變量的訪問。
public static synchronized void countNum(int n);
synchronized 方法的缺陷:若將一個大的方法聲明爲synchronized 將會大大影響效率。
典型地,若將線程類的方法 run() 聲明爲synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明爲synchronized ,並在主方法中調用來解決這一問題,但是 Java 爲我們提供了更好的解決辦法,那就是 synchronized 塊。
- synchronized 塊:
synchronized(SyncObject.Class) {
//允許訪問控制的代碼
}
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。
在使用synchronized 塊的時候,一定要遵循Brian同步規則,並對每個訪問臨界共享資源的方法都進行同步。
在圖中,紅框標識的部分方法,可以認爲已過時,不再使用。
(1)wait、notify、notifyAll是線程中通信可以使用的方法。線程中調用了wait方法,則進入阻塞狀態,只有等另一個線程調用與wait同一個對象的notify方法。這裏有個特殊的地方,調用wait或者notify,前提是需要獲取鎖,也就是說,需要在同步塊中做以上操作。
(2)join方法。該方法主要作用是在該線程中的run方法結束後,才往下執行。如以下代碼:
package com.thread.simple;
public class ThreadJoin {
public static void main(String[] args) {
Thread thread= new Thread(new Runnable() {
@Override
public void run() {
System.err.println("線程"+Thread.currentThread().getId()+" 打印信息");
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.err.println("主線程打印信息");
}
}
該方法顯示的信息是:線程8 打印信息
主線程打印信息
如果去掉其中的join方法,則顯示如下:
主線程打印信息
線程8 打印信息
(3)yield方法。這個是線程本身的調度方法,使用時你可以在run方法執行完畢時,調用該方法,告知你已可以出讓內存資源。
其他的線程方法,基本都會在日常中用到,如start、run、sleep,這裏就不再介紹。