java多執行緒基礎
本文就java多執行緒的基本使用方法、執行緒同步、執行緒狀態、執行緒函式等記錄一些總結。
程序:程序是資源分配的最小單位。每個程序都有獨立的程式碼和資料空間,程序間切換會有較大的開銷,一個程序往往包含多個執行緒。
執行緒:執行緒是CPU排程的最小單位。同一類執行緒共享程式碼和資料空間,每個執行緒有獨立的執行棧和程式計數器,執行緒切換開銷較小。
執行緒和程序一樣分為五個階段:建立、就緒、執行、阻塞、終止。
多程序指作業系統能同時執行多個任務。
多執行緒指同一程式有多個順序流執行。
實現多執行緒,通常有兩種手段:一種是繼承Thread類,另一種是實現Runable介面。
1. 繼承Thread類
* 定義類繼承Thread
* 重寫run方法
* 把新執行緒要做的事寫在run方法中
* 建立執行緒物件
* 開啟新執行緒, 內部會自動執行run方法
public class TestThread extends Thread { @Override public void run() { for(int i = 0; i < 1000; i++) { System.out.println("aaa"); } } } public class Test { public static void main(String[] args) { TestThread testThread = new TestThread(); testThread.start(); for(int i = 0; i < 1000; i++) { System.out.println("bbb"); } }
2. 實現Runable介面
* 定義類實現Runnable介面
* 實現run方法
* 把新執行緒要做的事寫在run方法中
* 建立自定義的Runnable的子類物件
* 建立Thread物件, 傳入Runnable
* 呼叫start()開啟新執行緒, 內部會自動呼叫Runnable的run()方法
public class TestRunable implements Runnable { @Override public void run() { for(int i = 0; i < 1000; i++) { System.out.println("aaa"); } } } public class Test { public static void main(String[] args) { //顯示建立執行緒 TestRunable testRunable = new TestRunable(); Thread thread = new Thread(testRunable); thread.start(); for(int i = 0; i < 1000; i++) { System.out.println("ccc"); } //匿名內部類 new Thread(new Runnable() { @Override public void run() { for(int i = 0; i < 1000; i++) { System.out.println("bbb"); } } }).start(); //Java8 Lambda表示式 new Thread(()->{ for(int i = 0; i < 1000; i++) { System.out.println("ddd"); } }).start(); } }
實現Runnable的原理 1 看Thread類的建構函式,傳遞了Runnable介面的引用 2 通過init()方法找到傳遞的target給成員變數的target賦值 3 檢視run方法,發現run方法中有判斷,如果target不為null就會呼叫Runnable介面子類物件的run方法
多執行緒兩種方式的區別 * 檢視原始碼的區別: a.繼承Thread : 由於子類重寫了Thread類的run(), 當呼叫start()時, 直接找子類的run()方法 b.實現Runnable : 建構函式中傳入了Runnable的引用, 成員變數記住了它, start()呼叫run()方法時 內部判斷成員變數Runnable的引用是否為空, 不為空編譯時看的是Runnable的run(),執行時執行的是子類的run()方法 * 繼承Thread * 好處是:可以直接使用Thread類中的方法,程式碼簡單 * 弊端是:如果已經有了父類,就不能用這種方法 * 實現Runnable介面 * 好處是:即使自己定義的執行緒類有了父類也沒關係,因為有了父類也可以實現介面,而且介面是可以多實現的 * 弊端是:不能直接使用Thread中的方法需要先獲取到執行緒物件後,才能得到Thread的方法,程式碼複雜
//獲取主函式執行緒的引用,並獲取名字
System.out.println(Thread.currentThread().getName());
Thread.currentThread().setName("主執行緒");
System.out.println(Thread.currentThread().getName());
new Thread(()->{
for(int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "ddd");
}
}).start();
休眠執行緒
public static void sleep(long millis)throws InterruptedException 在指定的毫秒數內讓當前正在執行的執行緒休眠(暫停執行) public static void sleep(long millis,int nanos)throws InterruptedException 在指定的毫秒數加指定的納秒數內讓當前正在執行的執行緒休眠(暫停執行)
new Thread() {
@Override
public void run() {
for (int i = 0; i < 3000; i++) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "...aaaaaa");
}
}
}.start();
守護執行緒
守護程序(Daemon)是執行在後臺的一種特殊程序。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。
void |
將該執行緒標記為守護執行緒或使用者執行緒。 |
當正在執行的執行緒都是守護執行緒時,Java 虛擬機器退出。 該方法必須在啟動執行緒前呼叫。 (設定一個執行緒為守護執行緒, 該執行緒不會單獨執行, 當其他非守護執行緒都執行結束後, 自動退出) 引數: on - 如果為 true,則將該執行緒標記為守護執行緒。
Thread thread1 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(getName() + "...aaaaaa");
}
}
};
Thread thread2 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName() + "...bbbbbb");
}
}
};
thread2.setDaemon(true);
thread1.start();
thread2.start();
Thread-1...bbbbbb
Thread-1...bbbbbb
Thread-1...bbbbbb
Thread-1...bbbbbb
Thread-1...bbbbbb
Thread-0...aaaaaa
Thread-0...aaaaaa
Thread-0...aaaaaa
加入執行緒
void |
join(long millis)
等待該執行緒終止的時間最長為 millis 毫秒。 |
void |
join(long millis, int nanos)
等待該執行緒終止的時間最長為 millis 毫秒 + nanos 納秒。 |
public static void main(String[] args) {
final Thread t1 = new Thread() {
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(getName() + "...aaaa");
}
}
};
Thread t2 = new Thread() {
public void run() {
for(int i = 0; i < 10; i++) {
if(i == 2) {
try {
//加入
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + "...bbbb");
}
}
};
t1.start();
t2.start();
}
}
禮讓執行緒
static void |
yield()
暫停當前正在執行的執行緒物件,並執行其他執行緒。 |
多執行緒同步程式碼塊
1.什麼情況下需要同步 * 當多執行緒併發, 有多段程式碼同時執行時, 我們希望某一段程式碼執行的過程中CPU不要切換到其他執行緒工作. 這時就需要同步. * 如果兩段程式碼是同步的, 那麼同一時間只能執行一段, 在一段程式碼沒執行結束之前, 不會執行另外一段程式碼. 2.同步程式碼塊 * 使用synchronized關鍵字加上一個鎖物件來定義一段程式碼, 這就叫同步程式碼塊 * 多個同步程式碼塊如果使用相同的鎖物件, 那麼他們就是同步的
3.使用synchronized關鍵字修飾一個方法, 該方法中所有的程式碼都是同步的