天天看點

并發程式設計筆記——第一章 并發程式設計基礎一、線程的建立與運作二、線程互動基礎方法三、上下文切換的概念四、死鎖條件五、守護線程和使用者線程六、ThreadLocal與InheritableThreadLocal

一、線程的建立與運作

  1. 實作Runable接口的run方法,作為Thread的構造參數
  2. 繼承Thread類,重寫run方法(好處:this等價于Thread.curentThread();壞處:java隻能單繼承)
  3. 使用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方法隻是讓線程處于就緒狀态,并非立即啟動

二、線程互動基礎方法

  1. obj.wait():線程挂起,直到 1.其他線程調用obj.notify()或obj.notifyAll(); 2.其他線程調用thread1.interrupt();
  2. obj.notify:喚醒一個obj挂起的線程,但該線程必須和其他線程競争obj鎖,搶到了才能繼續執行
  3. obj.notifyAll:喚醒所有
  4. thread1.join:等待線程執行完畢,阻塞
  5. Thread.sleep:線程會暫時讓出指定時間的執行權,但螢幕資源(如:鎖)仍持有,時間過後線程處于就緒狀态,擷取CPU資源後就可以繼續運作
  6. Thread.yield:暗示線程排程器目前線程請求讓出自己的CPU使用(即告訴線程排程器我剩下的時間片不要了,你執行下一輪排程吧),但是可能被忽略
  7. thread1.interrupt:給thread1設定中斷标志,當thread1調用wait,join,sleep而被阻塞挂起時,抛出異常,中斷線程
  8. Thread.isInterrupted():目前線程是否中斷,不清除标志
  9. Thread.interrupted():目前線程是否中斷,清除标志

tip:

  1. 調用wait或是notify方法之前都需要擷取該對象的螢幕鎖1:synchronized(obj){}; 2:synchronized void f(){wait();}; obj.f();
  2. 線程可能存在虛假喚醒,即沒有線程調用obj.notify的情況下被喚醒
  3. 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

  1. ThreadLocal:1、每個線程操作ThreadLocal共享變量時都會複制一個副本到本地線程,是以操作的都是本地線程變量;2、變量不支援父子線程的繼承
  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();
    }
}