1. 程式人生 > 其它 >C# 靜態擴充套件方法

C# 靜態擴充套件方法

Process與Thread

  • 說起程序,就不得不說下程式。程式是指令和資料的有序集合,其本身沒有任何執行的含義,是一個靜態的概念。
  • 而程序則是執行程式的一次執行過程,它是一個動態的概念。是系統資源分配的單位
  • 通常在一個程序中可以包含若干個執行緒,當然一個程序中至少有一個執行緒,不然沒有存在的意義。執行緒是CPU排程和執行的的單位。

注意:很多多執行緒是模擬出來的,真正的多執行緒是指有多個cpu,即多核,如伺服器。如果是模擬出來的多執行緒,即在一個cpu的情況下,在同一個時間點,cpu只能執行一個程式碼,因為切換的很快,所以就有同時執行的錯局。

  • 執行緒就是獨立的執行路徑;
  • 在程式執行時,即使沒有自己建立執行緒,後臺也會有多個執行緒,如主執行緒,gc執行緒;
  • main()稱之為主執行緒,為系統的入口,用於執行整個程式;
  • 在一個程序中,如果開闢了多個執行緒,執行緒的執行由排程器安排排程,排程器是與作業系統緊密相關的,先後順序是不能人文的干預的。
  • 對同一份資源操作時,會存在資源搶奪的問題,需要加入併發控制;
  • 執行緒會帶來額外的開銷,如cpu排程時間,併發控制開銷。
  • 每個執行緒在自己的工作記憶體互動,記憶體控制不當會造成資料不一致

執行緒的建立

  • Thread Class:繼承Thread類
  • Runnable介面:實現Runnable介面
  • Callable介面:實現Callable介面

Thread

  • 自定義執行緒繼承Thread類
  • 重寫run()方法。編寫執行緒執行體
  • 建立執行緒物件,呼叫start()方法啟動執行緒

實現Runnable介面

  • 定義My_Runnable類實現Runnable介面
  • 實現run()方法,編寫執行緒執行體
  • 建立執行緒物件,呼叫start()方法啟動執行緒

總結

  • 繼承Thread類
    • 子類繼承Thread具備多執行緒能力
    • 啟動執行緒:子類物件.start()
    • 不建議使用:避免OOP單繼承侷限性
  • 實現Runnable介面
    • 實現介面Runnable具有多執行緒能力
    • 啟動執行緒:傳入目標物件+Thread物件.start()
    • 推薦使用:避免單繼承的侷限性,靈活方便,同一個物件能夠被多個執行緒使用

警告:在多執行緒同時操作一個物件的時候如果不加鎖會導致執行緒不安全,資料紊亂

public class TestThread4 implements Runnable{
    private int tickerNums =10;

    @Override
    public void run() {
        while(true){
            if (tickerNums<=0){
                break;
            }

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到第"+tickerNums--+"張票");
        }
    }

    public static void main(String[] args) {
        TestThread4 testThread4 = new TestThread4();
        new Thread(testThread4,"wzb").start();
        new Thread(testThread4,"wh").start();
        new Thread(testThread4,"sxt").start();

    }
}

因此在進行的過程中需要對執行緒進行加鎖處理

public class TestThread4 implements Runnable{
    private int tickerNums =10;

    @Override
    public void run() {
        while(true){
            synchronized (this){
                if (tickerNums<=0){
                    break;
                }

                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"拿到第"+tickerNums--+"張票");
            }

        }
    }

    public static void main(String[] args) {
        TestThread4 testThread4 = new TestThread4();
        new Thread(testThread4,"wzb").start();
        new Thread(testThread4,"wh").start();
        new Thread(testThread4,"sxt").start();

    }
}

實現Callable介面

  1. 實現Callable介面,需要返回值型別
  2. 重寫call方法,需要丟擲異常
  3. 建立目標物件
  4. 建立執行服務:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交執行:Future result1 = ser.submit(t1);
  6. 獲取結果:boolean r1 = result1.get()
  7. 關閉服務:ser.shutdownNow();

Lambda表示式

  • 理解Functional Interface(函式式介面)是學習Java8 lambda的關鍵所在

  • 函式式介面的定義:

    • 任何介面,如果只包含唯一一個抽象方法,那麼它就是一個函式式介面。
    public interface Runnable{
    	public abstract void run();
    }
    
    • 對於函式式介面,可以通過lambda表示式來建立該介面的物件

執行緒狀態

  • 執行緒建立
    • Thread t = new Thread();執行緒一旦建立就進入到新生狀態
  • 就緒狀態
    • 當呼叫start()方法,執行緒立即進入就緒狀態,但不意味著立即排程執行
  • 阻塞狀態
    • 當呼叫sleep, wait或同步鎖定時,執行緒進入阻塞狀態,就是程式碼不往下執行,阻塞事件解除後,重新進入就緒狀態,等待CPU排程執行。
  • 執行狀態
    • 進入執行狀態,執行緒才真正執行執行緒體的程式碼塊
  • 執行緒死亡
    • 執行緒中斷或者結束,一旦進入死亡狀態,就不能再次啟動

執行緒停止

  • 建議執行緒正常停止-->利用次數,不建議死迴圈
  • 建議使用標誌位-->設定一個標誌位
  • 不要使用stop或者 destory 等過時或者 JDK 不建議使用的方法

執行緒休眠

  • sleep (時間)指定當前執行緒阻塞的毫秒數;
  • sleep存在異常Interrupted Exception;
  • sleep時間達到後執行緒進入就緒狀態;
  • sleep可以模擬網路延時,倒計時等。
  • 每一個物件都有一個鎖,sleep不會釋放鎖;

執行緒禮讓

  • 禮讓執行緒,讓當前正在執行的執行緒暫停,但不阻塞
  • 讓執行緒從執行狀態轉換為就緒狀態
  • 讓CPU 重新排程。禮讓不一定成功!看CPU心情
package com.zhou.state;

//測試禮讓執行緒
//禮讓不一定成功!看CPU心情
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"執行緒1").start();
        new Thread(myYield,"執行緒2").start();
    }

}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"開始執行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"停止執行");
    }
}

Join(執行緒強制執行)

  • Join合併執行緒,待此執行緒執行完成後,再執行其他執行緒其他執行緒阻塞
  • 可以想象成插隊
package com.zhou.state;

public class TestJoin implements Runnable{
    @Override
    public void run() {
        for(int i =0;i<100;i++){
            System.out.println("執行緒vip"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        for (int i = 0; i <1000 ; i++) {
            if(i==200){
                thread.join();
            }
            System.out.println("main"+i);
        }
    }
}

觀測執行緒狀態

package com.zhou.state;
//觀察測試執行緒狀態
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
           System.out.println("/////");
        });

        //觀察狀態
        Thread.State state= thread.getState();
        System.out.println(state);
        thread.start();
        state = thread.getState();
        System.out.println(state);

        while (state!=Thread.State.TERMINATED){
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);
        }
    }
}

執行緒優先順序

  • Java提供一個執行緒排程器來監控程式中啟動後進入就緒狀態的所有執行緒,執行緒排程器按照優先順序決定應該排程哪個執行緒來執行。
  • 執行緒的優先順序用數字表示,範圍從1~10.
    • Thread.MIN_PRIORITY = 1;
    • Thread.MAX_PRIORITY = 10;
    • Thread.NORM_PRIORITY = 5;
  • 使用以下方式改變或獲取優先順序
    • getPriority() . setPriority(int xxx)
package com.zhou.state;
//測試執行緒優先順序
public class TestPriority {
    public static void main(String[] args) {
        MyPriority myPriority = new MyPriority();
        //主執行緒優先順序
        System.out.println(Thread.currentThread().getName()+"---->"+Thread.currentThread().getPriority());
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);
        //先設定優先順序,再啟動
        t1.start();
        t2.setPriority(1);
        t2.start();
        t3.setPriority(4);
        t3.start();
        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();
        t5.setPriority(8);
        t5.start();
        t6.setPriority(7);
        t6.start();

    }
}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"---->"+Thread.currentThread().getPriority());
    }
}

守護(daemon)執行緒

  • 執行緒分為使用者執行緒守護執行緒
  • 虛擬機器必須確保使用者執行緒執行完畢
  • 虛擬機器不用等待守護執行緒執行完畢
  • 如.後臺記錄操作日誌,監控記憶體,垃圾回收(gc)等待..
package com.zhou.state;

public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//預設是false表示是使用者執行緒,正常的執行緒都是使用者執行緒...
        thread.start();//守護執行緒啟動
        new Thread(you).start();//使用者執行緒啟動
    }
}
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("守護執行緒啟動");
        }
    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 365000000; i++) {
            System.out.println("rr");
        }
        System.out.println("gg");
    }
}

執行緒同步

當多個執行緒操作同一個資源

併發

**同一個物件被多個執行緒同時操作 **

執行緒同步

  • 現實生活中,我們會遇到”同一個資源,多個人都想使用”的問題,比如,食堂排隊打飯,每個人都想吃飯﹐最天然的解決辦法就是,排隊。一個個來。
  • 處理多執行緒問題時,多個執行緒訪問同一個物件。並且某些執行緒還想修改這個物件。這時候我們就需要執行緒同步。執行緒同步其實就是一種等待機制,多個需要同時訪問此物件的執行緒進入這個物件的等待池形成佇列,等待前面執行緒使用完畢,下一個執行緒再使用。
  • 由於同一程序的多個執行緒共享同一塊儲存空間,在帶來方便的同時,也帶來了訪問衝突問題﹐為了保證資料在方法中被訪問時的正確性﹐在訪問時加入鎖機制synchronized ,當一個執行緒獲得物件的排它鎖﹐獨佔資源﹐其他執行緒必須等待,使用後釋放鎖即可.存在以下問題:
    • 一個執行緒持有鎖會導致其他所有需要此鎖的執行緒掛起;
    • 在多執行緒競爭下,加鎖﹐釋放鎖會導致比較多的上下文切換和排程延時,引起效能問題;
    • 如果一個優先順序高的執行緒等待一個優先順序低的執行緒釋放鎖會導致優先順序倒置,引起效能問題。

佇列加鎖

同步方法

  • 由於我們可以通過private關鍵字來保證資料物件只能被方法訪問,所以我們只需要針對方法提出一套機制,這套機制就是synchronized關鍵字,它包括兩種用法synchronized方法和synchronized塊。
  • synchronized方法控制對“物件”的訪問﹐每個物件對應一把鎖,每個synchronized方法都必須獲得呼叫該方法的物件的鎖才能執行,否則執行緒會阻塞,方法一旦執行,就獨佔該鎖,直到該方法返回才釋放鎖,後面被阻塞的執行緒才能獲得這個鎖,繼續執行。

缺陷:若將一個大的方法宣告為synchronized將會影響效率

package com.zhou.syn;

//不安全的買票
//執行緒不安全有負數

public class UnSafeBuyTicket {
    public static void main(String[] args){
        BuyTicket bt = new BuyTicket();
        new Thread(bt,"zqy").start();
        new Thread(bt,"wzb").start();
        new Thread(bt,"zxx").start();

    }
}
class BuyTicket implements Runnable{
    //票
    private int ticketNum =10;
    private boolean flag=true;//外部停止
    @Override
    public void run() {
        while(flag){
            try {
                buy();
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private synchronized void buy() throws InterruptedException {
        //判斷是否有票
        if(ticketNum<=0){
            flag = false;
            return;
        }
        //買票
        System.out.println(Thread.currentThread().getName()+"拿到第"+ticketNum--+"張票");
    }
}

同步塊

  • 同步塊:synchronized(Obj){}
  • Obj稱之為同步監視器
    • Obj可以是任何物件,但是推薦使用共享資源作為同步監視器
    • 同步方法中無需指定同步監視器﹐因為同步方法的同步監視器就是this ,就是這個物件本身,或者是class [反射中講解]
  • 同步監視器的執行過程
    • 第一個執行緒訪問,鎖定同步監視器,執行其中程式碼。
    • 第二個執行緒訪問,發現同步監視器被鎖定,無法訪問。
    • 第一個執行緒訪問完畢,解鎖同步監視器。
    • 第二個執行緒訪問,發現同步監視器沒有鎖,然後鎖定並訪問
package com.zhou.syn;
//不安全的取錢
//兩個人去銀行取錢,賬戶
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(1000000,"銀行卡");
        Bank you = new Bank(account,50000,"ni");
        Bank youF = new Bank(account,100000,"niF");
        you.start();
        youF.start();
    }
}
//賬戶
class Account{
    int money;
    String cardNum;
    public Account(int money,String cardNum){
        this.money =money;
        this.cardNum =cardNum;
    }
}
class Bank extends Thread{
    Account account;
    int drawingMoney;
    int nowMoney;
    public Bank(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    //取錢
    //synchronized 預設鎖的是this
    @Override
    public void run() {
        //鎖的物件就是變化的量,需要增刪改
        synchronized (account){
            if(account.money-drawingMoney<0){
                System.out.println(Thread.currentThread().getName()+"錢不夠去不了");
                return;
            }
            try {
                //sleep可以放大問題的發生性
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money-=drawingMoney;
            nowMoney+=drawingMoney;
            System.out.println(account.cardNum+"餘額為"+account.money+"元");
            //this.getName()=Thread.currentThread().getName()
            System.out.println(this.getName()+"手裡的錢為"+nowMoney);
        }
    }
}

死鎖

多個執行緒各自佔有一些共享資源,並且互相等待其他執行緒佔有的資源才能執行,而導致兩個或者多個執行緒都在等待對方釋放資源,都停止執行的情形。某一個同步塊同時擁有“兩個以上物件的鎖”時,就可能會發生“死鎖”的問題。

死鎖的避免方法

  • 產生死鎖的四個必要條件
    • 互斥條件:一個資源每次只能被一個程序使用
    • 請求與保持條件:一個程序因請求資源而阻塞時,對方已獲得的資源保持不放。
    • 不剝奪條件:程序已獲得的資源,在未使用完之前,不能強行剝奪。
    • 迴圈等待條件:若干程序之間形成一種頭尾相接的迴圈等待資源關係。

上面列出了死鎖的四個必要條件,我們只要想辦法破其中的任意一個或多個條件就可以避免死鎖發生

package com.zhou.lock;

//死鎖:多個執行緒互相抱著對方需要的資源,然後形成僵局
public class DeadLock {
    public static void main(String[] args) {
        Robot robot1 = new Robot(0,"Dave");
        Robot robot2 = new Robot(1,"Sun");
        robot1.start();
        robot2.start();
    }
}
class Body{

}
class Hand{

}
class Robot extends Thread{
    //需要的資源只有一份,用static來進行保證
    static Body body = new Body();
    static Hand hand = new Hand();

    int choice;//選擇
    String num;//編號
    public Robot(int choice,String num){
        this.choice=choice;
        this.num =num;
    }

    @Override
    public void run() {
        try {
            //拼接
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //拼接,互相持有對方的鎖,就是需要拿到對方的資源
    private void makeUp() throws InterruptedException {
        if(choice==0){
            synchronized (body){
                System.out.println(this.num+"獲得軀幹的鎖");
                Thread.sleep(1000);
            }
            synchronized (hand){
                System.out.println(this.num+"獲得四肢的鎖");
            }
        }
        if(choice==1){
            synchronized (hand){
                System.out.println(this.num+"獲得四肢的鎖");
                Thread.sleep(1000);
            }
            synchronized (body){
                System.out.println(this.num+"獲得軀幹的鎖");
            }
        }
    }
}

Lock(鎖)

  • 從JDK 5.0開始,Java提供了更強大的執行緒同步機制——通過顯式定義同步鎖物件來實現同步。同步鎖使用Lock物件充當
  • java.util.concurrent.locks.Lock介面是控制多個執行緒對共享資源進行訪問的工具。鎖提供了對共享資源的獨佔訪問,每次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源之前應先獲得Lock物件
  • ReentrantLock(可重入鎖)類實現了Lock,它擁有與synchronized相同的併發性和記憶體語義,在實現執行緒安全的控制中,比較常用的是ReentrantLock,可以顯式加鎖、釋放鎖。
package com.zhou.lock;

import java.util.concurrent.locks.ReentrantLock;

//測試Lock鎖
public class TestLock {
    public static void main(String[] args) {
        TestLock2 ts = new TestLock2();
        new Thread(ts,"sd").start();
        new Thread(ts,"sv").start();
        new Thread(ts,"sc").start();
    }
}
class TestLock2 implements Runnable{
    private int ticketNum =10;
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
           lock.lock();
           try {
               //加鎖
               if(ticketNum>0){
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(ticketNum--);
               }else {
                   break;
               }
           }finally {
               lock.unlock();//解鎖
           }

        }
    }
}

Lock與synchronized的對比

  • Lock是顯式鎖(手動開啟和關閉鎖,別忘記關閉鎖) synchronized是隱式鎖,出了作用域自動釋放
  • Lock只有程式碼塊鎖,synchronized有程式碼塊鎖和方法鎖
  • 使用Lock鎖,JVM將花費較少的時間來排程執行緒,效能更好。並且具有更好的擴充套件性(提供更多的子類)
  • 優先使用順序:Lock >同步程式碼塊(已經進入了方法體,分配了相應資源)>同步方法(在方法體之外)

執行緒通訊

  • 應用場景:生產者和消費者問題
    • 假設倉庫中只能存放一件產品﹐生產者將生產出來的產品放入倉庫﹐消費者將倉庫中產品取走消費.
    • 如果倉庫中沒有產品﹐則生產者將產品放入倉庫﹐否則停止生產並等待,直到倉庫中的產品被消費者取走為止.
    • 如果倉庫中放有產品﹐則消費者可以將產品取走消費,否則停止消費並等待,直到倉庫中再次放入產品為止.

執行緒通訊-分析

這是一個 執行緒同步問題,生產者和消費者共享同一個資源,並且生產者和消費者相互依賴,互為條件

  • 對於生產者,沒有生產產品之前,需要通知消費者等待,而生產了產品之後,又要馬上通知消費者消費
  • 對於消費者,在消費之後,要通知生產者已經結束消費,需要生產新的產品以供消費
  • 在生產者消費者問題中,僅有synchronized是不夠的
    • synchronized可阻止併發更新同一個共享資源,實現同步
    • synchronized不能用來實現不同執行緒之間的訊息傳遞(通訊)

解決方式1

併發協作模式“生產者/消費者模式”--->管程法

  • 生產者:負責生產資料的模組(可能是方法,物件,程序,執行緒);
  • 消費者:負責處理資料的模組(可能是方法,物件,執行緒,程序);
  • 緩衝區:消費者不能直接使用生產者的資料,他們之間有個“緩衝區”

生產者將生產好的資料放入緩衝區,消費者從緩衝區拿出資料

wait會釋放鎖, sleep不會釋放鎖

package com.zhou.lock;

import com.sun.corba.se.impl.orbutil.concurrent.Sync;

//測試:生產者消費者模型-->利用緩衝區解決:管程法
//生產者,消費者,產品,緩衝區
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        new Producer(synContainer).start();
        new Consumer(synContainer).start();

    }
}
class Producer extends Thread{
    SynContainer container;
    public Producer(SynContainer container){
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            try {
                container.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("生產了"+i+"只雞");
        }
    }
}
class Consumer extends Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container = container;
    }
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            try {
                System.out.println("消費了"+container.pop().id+"只雞");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Chicken{
   int id;
   public Chicken(int id){
       this.id=id;
   }
}
class SynContainer{
    Chicken[] chickens = new Chicken[10];
    int num = 0;
    public synchronized void push(Chicken chicken) throws InterruptedException {
        //如果容器滿了就需要等待消費者
        if (num == chickens.length-1){
            //等待生產
            this.wait();
        }
        //如果沒有滿,就需要丟入產品
        chickens[num] = chicken;
        num++;
        //通知消費者消費
        this.notifyAll();

    }
    public synchronized Chicken pop() throws InterruptedException {
        //判斷能否消費
        if(num==0){
            //等待生產者生產
            this.wait();
        }
        //如果可以消費
        num--;
        Chicken chicken =chickens[num];
        //吃完了通知生產者生產

        this.notifyAll();
        return chicken;

    }
}

解決方式2

併發協作模型“生產者/消費者模式”--->訊號燈

package com.zhou.lock;

public class TestPc2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生產者-->演員
class Player extends Thread{
    TV tv = new TV();
    public Player(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            if(i%2==0){
                try {
                    this.tv.play("sdsds");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                try {
                    this.tv.play("sdaqqa");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

//消費者-->觀眾
class Watcher extends Thread{
    TV tv = new TV();
    public Watcher(TV tv){
        this.tv=tv;
    }
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            try {
                this.tv.watch();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

//產品-->節目
class TV{
    //演員表演,觀眾等待 T
    //觀眾觀看,演員等待 F
    String voice;//表演節目
    boolean flag =true;

    //表演
    public synchronized void play(String voice) throws InterruptedException {
        if(!flag){
            this.wait();
        }
        System.out.println("演員表演了:"+voice);
        //通知觀眾觀看
        this.notifyAll();
        this.voice = voice;
        this.flag =! this.flag;
    }
    //觀看
    public synchronized void watch() throws InterruptedException {
        if(flag){
            this.wait();
        }
        System.out.println("觀看了"+voice);
        this.notifyAll();
        this.flag =! this.flag;
    }
}

使用執行緒池

  • 背景:經常建立和銷燬、使用量特別大的資源,比如併發情況下的執行緒,對效能影響很大。
  • 思路:提前建立好多個執行緒,放入執行緒池中,使用時直接獲取,使用完放回池中。可以避免頻繁建立銷燬、實現重複利用。類似生活中的公共交通工具。
  • 好處:
    • 提高響應速度(減少了建立新執行緒的時間)
    • 降低資源消耗(重複利用執行緒池中執行緒,不需要每次都建立)
    • 便於執行緒管理(...)
      • corePoolSize:核心池的大小
      • maximumPoolSize:最大執行緒數
      • keepAliveTime:執行緒沒有任務時最多保持多長時間後會終止
  • JDK 5.0起提供了執行緒池相關API: ExecutorService和Executors
  • ExecutorService:真正的執行緒池介面。常見子類ThreadPoolExecutor
    • void execute(Runnable command)︰執行任務/命令,沒有返回值,一般用來執行Runnable
    • Future submit(Callable task):執行任務,有返回值,一般又來執行Callable
    • void shutdown():關閉連線池
  • Executors:工具類、執行緒池的工廠類,用於建立並返回不同型別的執行緒池
package com.zhou.pool;

import java.util.concurrent.*;

public class TestPool {
    public static void main(String[] args) {
        //1.建立服務,建立執行緒池
        //newFixedThreadPool 引數為:執行緒池大小
        ExecutorService ser = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());

        //執行
        ser.execute(new MyThread());
        ser.execute(new MyThread());
        ser.execute(new MyThread());
        ser.execute(new MyThread());

        //2.關閉連線
        ser.shutdown();

    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}