一、線程的建立與運作
- 實作Runable接口的run方法,作為Thread的構造參數
- 繼承Thread類,重寫run方法(好處:this等價于Thread.curentThread();壞處:java隻能單繼承)
- 使用FutureTask方式(好處,可以擷取傳回結果)
樣例3:
import javax.swing.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Main extends JFrame {
public static void main(String[] args) {
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
new Thread(futureTask).start();
try {
// 阻塞的
String result = futureTask.get();
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
static class CallerTask implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(3000L);
return "AAA";
}
}
}
tip:start方法隻是讓線程處于就緒狀态,并非立即啟動
二、線程互動基礎方法
- obj.wait():線程挂起,直到 1.其他線程調用obj.notify()或obj.notifyAll(); 2.其他線程調用thread1.interrupt();
- obj.notify:喚醒一個obj挂起的線程,但該線程必須和其他線程競争obj鎖,搶到了才能繼續執行
- obj.notifyAll:喚醒所有
- thread1.join:等待線程執行完畢,阻塞
- Thread.sleep:線程會暫時讓出指定時間的執行權,但螢幕資源(如:鎖)仍持有,時間過後線程處于就緒狀态,擷取CPU資源後就可以繼續運作
- Thread.yield:暗示線程排程器目前線程請求讓出自己的CPU使用(即告訴線程排程器我剩下的時間片不要了,你執行下一輪排程吧),但是可能被忽略
- thread1.interrupt:給thread1設定中斷标志,當thread1調用wait,join,sleep而被阻塞挂起時,抛出異常,中斷線程
- Thread.isInterrupted():目前線程是否中斷,不清除标志
- Thread.interrupted():目前線程是否中斷,清除标志
tip:
- 調用wait或是notify方法之前都需要擷取該對象的螢幕鎖1:synchronized(obj){}; 2:synchronized void f(){wait();}; obj.f();
- 線程可能存在虛假喚醒,即沒有線程調用obj.notify的情況下被喚醒
- wait方法隻會釋放目前對象的螢幕鎖,其他螢幕鎖仍處于持狀态
三、上下文切換的概念
- CUP資源的配置設定采用了時間片輪轉的政策,也就是給每個線程配置設定一個時間片,線程在時間片内占用CPU執行任務。目前線程使用完時間片後,就會處于就緒狀态并讓出CPU給其他線程占用,這就是上下文切換。切換時需要儲存目前線程的執行現場,當再次執行時根據儲存的資訊恢複執行現場。
切換時機:1、時間片用完處于就緒狀态; 2、線程被其他線程中斷
四、死鎖條件
- 互斥條件:鎖資源有排他性
- 請求并持有條件:1、有鎖資源》》2、請求其他鎖》》3、其他鎖被占有》》4、阻塞同時不釋放鎖資源
- 不可剝奪條件:擷取到的鎖資源在自己用完之前不能被其他線程占有
- 環路等待條件:A等B,B等C,C等A
避免死鎖的方法:破壞四個條件之一,目前隻能破壞請求并持有和環路等待
- 例如:資源申請有序性原則(隻有擷取了第n-1個資源才能申請擷取第n個資源)
五、守護線程和使用者線程
- 當最後一個非守護線程結束時,JVM會正常退出
- 設定守護線程:thread.setDaemon(true);
六、ThreadLocal與InheritableThreadLocal
- ThreadLocal:1、每個線程操作ThreadLocal共享變量時都會複制一個副本到本地線程,是以操作的都是本地線程變量;2、變量不支援父子線程的繼承
- InheritableThreadLocal:支援父子線程的繼承,是在new Thread()的時候複制父線程inheritableThreadLocals的一個副本到子線程,本質上還是互相獨立的
例ThreadLocal:
public class Main extends JFrame {
static ThreadLocal<String> localValue = new ThreadLocal<>();
public static void main(String[] args) throws FileNotFoundException, InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
localValue.set("1");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 為空
System.out.println(localValue.get());
}
});
thread1.start();
thread2.start();
}
}