一、線程和進程
進程:程序運行資源分配的最小單位,進程內部有多個線程,會共享這個進程的資源。
線程:CPU調度的最小單位,必須依賴進程而存在,線程之間共享進程的資源。
二、線程的開啓方式
1.繼承Thread
public class UserThread extends Thread {
@Override
public void run() {
System.out.println("通過繼承Thread類開啓新線程!");
}
}
2.實現Runnable接口
public class UseRunnable implements Runnable {
@Override
public void run() {
System.out.println("通過實現Runnable接口開啓新線程!");
}
}
3.實現Callable接口
public class UseCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("通過實現Callable接口開啓新線程!");
return "通過實現Callable接口開啓新線程!";
}
}
測試代碼:
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.使用繼承Thread的方式開啓新線程
Thread thread = new UserThread();
thread.start();
//2.使用實現Runnable接口的方式開啓新線程
Thread runnable = new Thread(new UseRunnable());
runnable.start();
//3.使用實現Callable接口的方式開啓新線程
//包裝Callable爲Runnable的類,因爲Thread不接受直接傳遞Callable,FutureTask也爲Runnable的子類
//FutureTask會在Runnable的run方法中調用Callable的call方法,從而達到開啓新線程的效果。
FutureTask<String> futureTask = new FutureTask<>(new UseCallable());
Thread callable = new Thread(futureTask);
callable.start();
//通過FutureTask的get方法拿到返回值,如果Callable沒執行完沒獲取到返回值,會阻塞。
String result = futureTask.get();
System.out.println(result);
}
注意:使用實現Callable接口的方式,需要使用FutureTask包裝Callable,再傳入至Thread中。
三、線程有關的API
Thread方法:
start():開啓並運行一個新的線程。
getId():返回該線程的id
getName():返回該線程的名字
getPriority():返回該線程的優先級
getState():返回該線程的狀態
isAlive():檢測該線程是否還活着
isDaemon():檢測該線程是否爲守護線程
join():在A線程中調用B線程的join()方法,則爲A線程等待B線程執行完後再執行,即A線程阻塞,直到B線程執行完。
Thread.yield():靜態方法,使當前線程放棄cpu的執行權重新進入就緒狀態,注意該方法持有的鎖是不釋放的。
Thread.sleep(long millis):靜態方法,使當前線程睡眠,單位毫秒,注意該方法持有的鎖是不釋放的。
interrupt():將該線程中斷(實際並不會中斷,只是將中斷標誌設置爲true),如果線程正處在sleep(),join(),wait()方法中時(也就是阻塞時)調用該方法,該方法會拋出異常。
isInterrupted():檢測該線程是否已經中斷,返回true或false。
Thread.interrupted():靜態方法,檢測當前線程是否已經中斷,返回true或false。
Thread.currentThread():返回對當前正在運行的線程。
Object方法:
wait():使當前持有鎖的線程進入等待狀態,注意該方法會釋放持有的鎖。
wait(long):使當前持有鎖的線程進入等待狀態,並設置超時時間,超時後會自動喚醒,注意該方法會釋放持有的鎖。
notify():隨機喚醒一個通過該對象加鎖並且處於等待中的線程。
notifyAll():喚醒所有通過該對象加鎖並且等待中的線程。
四、線程的生命週期
1.新建:就是new出來的線程
2.就緒:調用線程的start()方法後,這時線程處於等待CPU分配資源階段,誰分配到CPU資源誰先開始執行。
3.運行:就緒線程被調度並獲得CPU資源時,則進入運行狀態,運行run方法中的代碼。
4.阻塞:在運行狀態的時候,可能因爲某些原因導致運行狀態的線程變成了阻塞狀態,比如調用了該線程的sleep()、wait()或調用其它線程的join(),使當前線程進入了阻塞狀態,這個時候需要其他機制將處於阻塞狀態的線程喚醒,比如調用notify()、notifyAll()方法或等待。注意,喚醒的線程不會立刻執行run(),它們要再次等待CPU分配資源後再次進入運行狀態。
5.銷燬:如果線程正常執行完畢或拋出異常導致結束,那麼線程就要被銷燬,釋放資源。
五、synchronized內置鎖
synchronized爲對象鎖,鎖的是對象的實例,常說的類鎖其實也是對象鎖,鎖的是類的class對象,每個類的class對象在虛擬機中只有一個,所以類鎖也只有一個。
在靜態方法定義中加鎖,則是爲對該類的class對象加鎖,在普通方法定義中加鎖,則是爲當前this對象進行加鎖。
六、ThreadLocal
線程變量,可以理解爲是個map,類型 Map<Thread,Object>(實際上Key是ThreadLocal,是對Thread的包裝),適合存儲小對象,不會有線程安全問題,ThreadLocal的作用是爲了能夠在當前線程中有屬於自己的變量。