1. 程式人生 > 其它 >多執行緒 學習筆記(2021.10.13~17)

多執行緒 學習筆記(2021.10.13~17)

多執行緒

目錄

一、執行緒簡介

任務、程序、執行緒、多執行緒

1. 多工

同時進行兩個任務:邊玩遊戲邊聽歌

2. 多執行緒

一條路只能走一輛車,想要一條路走兩輛車需要分出很多道,這樣太能同時開兩輛車(類比)

3. 普通方法和多執行緒

4. 程式、程序、執行緒

  1. 執行的程式就是程序
  2. 一個程序可以有多個執行緒,如:視訊中可以同時看影象,聽聲音,看彈幕。
  3. 程序包含至少一個執行緒,如果是模擬出的多執行緒,即在一個CPU中快速切換,感覺到是同時執行

5. 核心概念

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

二、執行緒建立

1. 三種建立方式

  • Thread class:繼承Thread類(重點)

  • package com.xiaowei9s.lesson01;
    
    //建立執行緒方式一:繼承Thread類,重寫run方法,呼叫start開啟執行緒
    
    //總結:執行緒不一定start後直接執行,由CPU排程
    
    public class ThreadTest1 extends Thread {
        public static void main(String[] args) {
            ThreadTest1 threadTest1 = new ThreadTest1();
            threadTest1.start();
    
            for (int i = 0; i < 200; i++) {
                System.out.println("main"+i);
            }
    
    
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 200; i++) {
                System.out.println("run"+i);
            }
        }
    }
    
  • Runnable介面:實現Runnable介面(重點)

  • package com.xiaowei9s.lesson01;
    
    //建立執行緒方式一:繼承Thread類,重寫run方法,呼叫start開啟執行緒
    
    //總結:執行緒不一定start後直接執行,由CPU排程
    
    public class ThreadTest3 implements Runnable {
        public static void main(String[] args) {
            ThreadTest3 threadTest3 = new ThreadTest3();
            new Thread(threadTest3).start();
    
            for (int i = 0; i < 2000; i++) {
                System.out.println("main"+i);
            }
    
    
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 2000; i++) {
                System.out.println("run"+i);
            }
        }
    }
    

    初時多併發:

    package com.xiaowei9s.lesson01;
    
    //多個執行緒同時操作一個物件
    
    //發現問題,執行緒不安全了
    //模擬同時買票
    public class ThreadTest4 implements Runnable {
        private int ticketNum=10;
        public static void main(String[] args) {
            ThreadTest4 tt1 = new ThreadTest4();
            ThreadTest4 tt2 = new ThreadTest4();
    
            new Thread(tt1,"小明").start();
            new Thread(tt1,"老師").start();
            new Thread(tt1,"黃牛").start();
    
    
        }
    
    
        @Override
        public void run() {
            while (ticketNum>=0){
                try {
                    Thread.sleep(200);
                }catch (Exception e){
                    System.out.println(e);
                }
    
                System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum+"號票");
                ticketNum--;
            }
    
        }
    }
    

    龜兔賽跑

    package com.xiaowei9s.lesson01;
    
    //龜兔賽跑
    //跑一百米
    //兔子睡覺並且輸掉遊戲
    public class ThreadTest5 implements Runnable{
        private String winner;
    
        public static void main(String[] args) {
            ThreadTest5 threadTest5 = new ThreadTest5();//共用一個物件
            new Thread(threadTest5,"兔子").start();
            new Thread(threadTest5,"烏龜").start();
    
        }
    
        private boolean gameOver(int step){//是否結束遊戲
            if (winner!=null){
                return true;
            }
            if (step==0){
                winner = Thread.currentThread().getName();
                System.out.println(winner+"贏了");
                return true;
            }
            return false;
        }
    
        @Override
        public void run() {
            int step = 100;//雖然共用一個物件,但是方法是兩個,每個執行緒呼叫方法時方法時獨立得方法,方法內的資源不共用
            while(true){
                try {//延時
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(gameOver(step)){
                    break;
                }
                if (Thread.currentThread().getName().equals("兔子")&&step%10==0){//兔子跑十下休息以下
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"還剩"+step+"步");
                step--;
            }
    
        }
    }
    
  • Callable介面:實現Callable介面(瞭解)

  • package com.xiaowei9s.lesson01;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.util.concurrent.*;
    
    /*
        執行緒建立方式三,實現Callable介面
        優勢:
        1. 自定義返回值型別
        2. 可以丟擲異常
    */
    
    public class ThreadTest6 implements Callable<Boolean> {
        public ThreadTest6(String name, String url) {
            this.name = name;
            this.url = url;
        }
    
        public String name;
        public String url;
    
    
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //建立執行緒池
            ExecutorService exs = Executors.newFixedThreadPool(3);
    
            //提交執行緒
            Future<Boolean> submit = exs.submit(new ThreadTest6("1.jpg", "https://pics6.baidu.com/feed/42a98226cffc1e17b881c7a94554b40a728de9bd.jpeg?token=6f6f69fad743d5bf129581dcbe35269f"));
            Future<Boolean> submit1 = exs.submit(new ThreadTest6("2.jpg","https://pics1.baidu.com/feed/4b90f603738da97767ecd34db795ba108418e3e6.jpeg?token=2695638344602480017c59554e078749"));
            Future<Boolean> submit2 = exs.submit(new ThreadTest6("3.jpg","https://pics3.baidu.com/feed/377adab44aed2e73899a0cee8ac5e38285d6faca.jpeg?token=2bc485aa1ae4cea267fb997488b209d5"));
    
            //獲得執行緒結果
            Boolean aBoolean = submit.get();
            Boolean aBoolean1 = submit1.get();
            Boolean aBoolean2 = submit2.get();
    
            //關閉服務
            exs.shutdownNow();
    
        }
    
        @Override
        public Boolean call() throws Exception {
            MyDownloader downloader = new MyDownloader();
            downloader.download(url,name);
            return true;
        }
    
        class MyDownloader{
            public void download(String url,String name){
                try {
                    FileUtils.copyURLToFile(new URL(url),new File(name));
                    System.out.println(name);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

推薦使用Runnable介面,因為Runnable介面可以解耦,繼承Thread類有侷限性

三、靜態代理

讓主要的類專注於處理實現關鍵功能,例如

new Thread(()->System.out.println("run()方法中的事情")).start();
package com.xiaowei9s.lesson02;

public class StaticMode {
    public static void main(String[] args) {

        ToolMan toolMan = new ToolMan(new You());
        toolMan.toDo();
    }
}


class You implements Work{

    @Override
    public void toDo() {
        System.out.println("你做了關鍵的事情");
    }
}

class ToolMan implements Work{
    public ToolMan(Work toWork) {
        this.toWork = toWork;
    }

    public Work toWork;//把工具人幫助的人加入工具人,兩個人實現了同一個介面就是靜態代理

    @Override
    public void toDo() {
        System.out.println("工具人先做了輔助的事情");
        toWork.toDo();
    }
}

interface Work{
    public void toDo();
}

四、Lambda表示式

  • 函式式介面:只包含唯一一個抽象方法,它就是一個函式式介面
  • 對於函式式介面,我們可以使用Lambda表示式建立該介面的物件
package com.xiaowei9s.lesson02;


//逐漸匯出Lambda表示式
//實現類->匿名內部類->Lambda表示式

public class LambdaDemo {
    public static void main(String[] args) {
        //匿名內部類
        FunMode fm = new FunMode() {
            @Override
            public void toDo() {
                System.out.println("使用匿名內部類實現");
            }
        };

        fm.toDo();
        new Test1().toDo();

        //Lambda表示式
        fm = ()-> System.out.println("使用Lambda表示式實現");
        fm.toDo();

    }

    interface FunMode{//函式式介面
        public void toDo();
    }


}
///使用實現類
class Test1 implements LambdaDemo.FunMode {

    @Override
    public void toDo() {
        System.out.println("使用實現類實現");
    }
}

五、執行緒停止

執行緒狀態:

執行緒方法:

1. 停止執行緒的方法

  • 官方不建議使用JDK中的方法

  • 使用一個停止標誌位實現執行緒停止

  • package com.xiaowei9s.lession03;
    
    public class TestStop {
        public static void main(String[] args) {
            Arun arun = new Arun();
            new Thread(arun).start();
    
            for (int i = 0; i < 1000; i++) {
                if (i==500){
                    arun.stop();
                    System.out.println("stop");
                }
                System.out.println(i);
            }
        }
    }
    class Arun implements Runnable{
        private boolean isStop = true;
    
        @Override
        public void run() {
            int i = 0;
            while (isStop){
    
                System.out.println("I am running..."+i++);
            }
            System.out.println("ji");
        }
    
        public void stop(){
            this.isStop = false;
        }
    
    
    }
    

六、執行緒休眠

  • sleep(時間)表示當前執行緒休眠時間,單位:毫秒
  • sleep存在異常InterruptedExpection;
  • sleep時間達到後,執行緒重新進入就緒狀態;
  • sleep可以模擬網路延時、倒計時等等;
  • 每一個物件都有鎖,sleep不會釋放鎖;(記住、不用理解)

Demo:模擬網路延遲:

package com.xiaowei9s.lession03;

//模擬網路延遲
public class TestSleep implements Runnable{
    public int ticket = 10;

    public static void main(String[] args) {
        TestSleep testSleep = new TestSleep();
        new Thread(testSleep,"小明").start();
        new Thread(testSleep,"小紅").start();
        new Thread(testSleep,"老師").start();
        new Thread(testSleep,"黃牛").start();

    }

    @Override
    public void run() {
        while (ticket>=0){

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->買了第"+ticket+"號票");
            ticket--;
        }


    }
}

Demo:模擬時鐘:

package com.xiaowei9s.lession03;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TestTime {
    public static void main(String[] args) throws InterruptedException {

        while (true){
            Date date = new Date(System.currentTimeMillis());
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            Thread.sleep(1000);
        }

    }
}

七、執行緒禮讓

  • 讓當前程序從執行變成就緒狀態
  • 當前程序不阻塞,依然擁有資源
  • 不一定了禮讓成功,變成就緒的起跑線,cpu依然有可能讓禮讓的執行緒重新進入執行
package com.xiaowei9s.lession03;

public class TestYeild {
    public static void main(String[] args) {
        new Thread(new MyYield(),"A").start();
        new Thread(new MyYield(),"B").start();
    }
}

class MyYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"執行緒開始");
        Thread.yield();//禮讓
        System.out.println(Thread.currentThread().getName()+"執行緒結束");
    }
}

八、執行緒強制執行(執行緒插隊)

直接加入指定程序,插隊!

package com.xiaowei9s.lession03;

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

    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}

九、執行緒狀態檢測

執行緒在停止後,不能再次start()。

package com.xiaowei9s.lession03;

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("THE END");
        });

        Thread.State state = thread.getState();
        System.out.println(state);

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

        state = thread.getState();
        System.out.println(state);

    }
}

十、執行緒優先順序

數字越小,優先順序越低,10優先順序最高

main執行緒預設優先順序是5

優先順序只是意味著cpu排程概率,優先順序低也有概率會先執行

優先順序設定在start之前

package com.xiaowei9s.lession03;

public class TestPriority implements Runnable {

    public static void main(String[] args) {
        TestPriority testPriority = new TestPriority();
        Thread a = new Thread(testPriority, "a");
        Thread b = new Thread(testPriority, "b");
        Thread c = new Thread(testPriority, "c");
        Thread d = new Thread(testPriority, "d");
        Thread e = new Thread(testPriority, "e");
        Thread f = new Thread(testPriority, "f");
        Thread g = new Thread(testPriority, "g");
        Thread h = new Thread(testPriority, "h");
        Thread i = new Thread(testPriority, "i");

        System.out.println("main-->"+Thread.currentThread().getPriority());
        a.setPriority(1);
        b.setPriority(2);
        c.setPriority(3);
        d.setPriority(4);
        e.setPriority(6);
        f.setPriority(7);
        g.setPriority(8);
        h.setPriority(9);

        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
        f.start();
        g.start();
        h.start();
        i.start();

    }


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

十一、守護執行緒Deamon

一旦執行緒被設定成守護執行緒,則主執行緒不會等待守護執行緒結束。

使用方法

thread.setDeamon(true);//將該執行緒設定成守護執行緒

Demo:

package com.xiaowei9s.lession03;

public class TestDeamon {
    public static void main(String[] args) {
        You you = new You();
        God god = new God();


        Thread gt = new Thread(god);
        gt.setDaemon(true);
        gt.start();
        new Thread(you).start();
    }
}

class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 35000; i++) {
            System.out.println("你活了:" + i + "天");
        }
    }
}

class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("你的家人朋友守護著你");
        }
    }
}

十二、執行緒同步機制

多個執行緒操作一個資源

1. 併發

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

在處理執行緒時多個執行緒同時訪問一個物件,這個時候我們需要執行緒同步。

執行緒同步其實就是等待機制,多個執行緒進入該物件的執行緒池中等待形成佇列,進行順序訪問。

2. 佇列和鎖

佇列:保證訪問順序

鎖:保證物件同一時間只能有一個物件訪問

鎖機制:synchronized,主要就是排它鎖

3. 效能問題

進行執行緒同步會導致效能下降和優先順序倒置問題

4. 三大不安全案例

  • 不安全的買票

  • package com.xiaowei9s.syn;
    
    public class UnsafeTicket {
        public static void main(String[] args) {
            BuyTicket buyTicket = new BuyTicket();
            new Thread(buyTicket,"小明").start();
            new Thread(buyTicket,"小黃").start();
            new Thread(buyTicket,"小紅").start();
        }
    }
    
    class BuyTicket implements Runnable{
        int numTicket = 10;
        boolean flag = true;
    
        //買票
        public void buy(){
            if (numTicket<=0){
                flag = false;
            }
    
            System.out.println(Thread.currentThread().getName()+"買到了"+numTicket--+"票");
    
        }
    
        @Override
        public void run() {
            while (flag){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                buy();
            }
        }
    }
    
  • 不安全的取錢

  • package com.xiaowei9s.syn;
    
    public class UnsafeBank {
        public static void main(String[] args) {
            Account account = new Account("結婚基金", 1000);
            Drawing d1 = new Drawing(account, 500, "老公");
            Drawing d2 = new Drawing(account, 800, "老婆");
    
            d1.start();
            d2.start();
        }
    
    }
    
    //賬戶
    class Account{
        public String name;
        public int monny;
        public Account(String name , int monny){
            this.name = name;
            this.monny = monny;
        }
    }
    
    //銀行取錢
    class Drawing extends Thread{
        Account account;
        int nowMonny;
        int getMonny;
    
        public Drawing(Account account, int getMonny, String name){
            super(name);
            this.account = account;
            this.getMonny = getMonny;
        }
    
        //取錢
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (account.monny - getMonny < 0){
                System.out.println("賬戶裡還有" + account.monny + "元,錢不夠了");
                return;
            }
            nowMonny += getMonny;
            account.monny -= getMonny;
            System.out.println(Thread.currentThread().getName()+"取了"
                    + getMonny+"元,還剩"+account.monny+"元,現在ta有"
                    + nowMonny+"元");
        }
    }
    
  • 不安全的列表

  • package com.xiaowei9s.syn;
    
    import java.util.ArrayList;
    
    public class UnsafeList {
        public static void main(String[] args) throws InterruptedException {
            ArrayList arrayList = new ArrayList();
            for (int i = 0; i < 10000; i++) {
                new Thread(()->{
                    arrayList.add(1);
                }).start();
            }
            Thread.sleep(3000);
            System.out.println(arrayList.size());
        }
    }
    

5. 同步方法

在方法中加入同步方法關鍵字synchronized,這個方法就變成了同步方法,這個方法只能在獲得物件的鎖的時候才能執行,執行完成才返回這把鎖。這把鎖鎖的是this,也就是方法體所在的物件。

缺陷:影響效率

比如之前的買票,在run方法加入關鍵字即可同步

package com.xiaowei9s.syn;

public class UnsafeTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"小明").start();
        new Thread(buyTicket,"小黃").start();
        new Thread(buyTicket,"小紅").start();
    }
}

class BuyTicket implements Runnable{
    int numTicket = 10;
    boolean flag = true;

    //買票
    public void buy(){
        System.out.println(Thread.currentThread().getName()+"買到了第"+numTicket--+"票");
        if (numTicket==0){
            flag = false;
        }
    }

    @Override
    public synchronized void run() {//加入關鍵字
        while (flag){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            buy();
        }
    }
}

6. 同步塊

synchronized(Obj){};//Obj是需要監視的物件,也就是對這個物件加上鎖

例如銀行取錢,我們只需要對賬戶加上監視,讓它同步即可:

package com.xiaowei9s.syn;

public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account("結婚基金", 1000);
        Drawing d1 = new Drawing(account, 500, "老公");
        Drawing d2 = new Drawing(account, 800, "老婆");

        d1.start();
        d2.start();
    }

}

//賬戶
class Account{
    public String name;
    public int monny;
    public Account(String name , int monny){
        this.name = name;
        this.monny = monny;
    }
}

//銀行取錢
class Drawing extends Thread{
    Account account;
    int nowMonny;
    int getMonny;

    public Drawing(Account account, int getMonny, String name){
        super(name);
        this.account = account;
        this.getMonny = getMonny;
    }

    //取錢
    @Override
    public void run() {

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        synchronized (account){
            if (account.monny - getMonny < 0){
                System.out.println("賬戶裡還有" + account.monny + "元,錢不夠了");
                return;
            }
            nowMonny += getMonny;
            account.monny -= getMonny;
            System.out.println(Thread.currentThread().getName()+"取了"
                    + getMonny+"元,還剩"+account.monny+"元,現在ta有"
                    + nowMonny+"元");
        }

    }
}

7. CopyOnWriteArrayList

就是一個JUC併發程式設計中的一個列表類。

他內部實現了一個鎖和同步的功能,不用自己去實現同步。

十三、死鎖

當一個同步塊擁有兩個以上的物件的鎖的時候有可能發生。

因為一個物件被一個執行緒佔有,而另一個物件被另一個執行緒佔有,兩個執行緒都不願意放手,就誰都做不了事情。

package com.xiaowei9s.syn;

public class DieLock {
    public static void main(String[] args) {
        Key1 key1 = new Key1();
        Key2 key2 = new Key2();
        LockNeedK1K2 lockNeedK1K2 = new LockNeedK1K2(key1, key2);
        new Thread(lockNeedK1K2,"小紅").start();
        new Thread(lockNeedK1K2,"小黃").start();
    }
}

class LockNeedK1K2 implements Runnable{
    public LockNeedK1K2(Key1 k1, Key2 k2) {
        this.k1 = k1;
        this.k2 = k2;
    }

    public Key1 k1;
    public Key2 k2;

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("小紅")){
            synchronized (k1){
                System.out.println(Thread.currentThread().getName() + "獲得了第一個密碼");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (k2){
                    System.out.println(Thread.currentThread().getName() + "獲得了第二個密碼");
                    System.out.println("密碼是:" + k1.password1 + k2.password2);
                    System.out.println("打開了寶箱");
                }
            }
        }
        if(Thread.currentThread().getName().equals("小黃")){
            synchronized (k2){
                System.out.println(Thread.currentThread().getName() + "獲得了第二個密碼");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (k1){
                    System.out.println(Thread.currentThread().getName() + "獲得了第一個密碼");
                    System.out.println("密碼是:" + k1.password1 + k2.password2);
                    System.out.println("打開了寶箱");
                }
            }
        }

    }
}

class Key1{
    String password1 = "123";
}

class Key2{
    String password2 = "321";
}

避免死鎖:不要在同步塊中再次嵌入同步塊!

十四、Lock(鎖)

demo:

package com.xiaowei9s.syn;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        Lock1 lock1 = new Lock1();
        new Thread(lock1,"hong").start();
        new Thread(lock1,"huang").start();
        new Thread(lock1,"jun").start();
    }
}

class Lock1 implements Runnable{
    public int ticket = 10;
    public ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {

        try {
            lock.lock();
            while (ticket>0){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+ticket);
                ticket--;
            }
        }catch (Exception e){

        }finally {
            lock.unlock();
        }



    }
}

十五、執行緒協作

生產者消費者問題

相關方法:

解決方法:

  • 緩衝區法,設定緩衝區(管程法)

  • package com.xiaowei9s.syn;
    
    public class TestPC {
        public static void main(String[] args) {
            SynContain synContain = new SynContain();
            new Producer(synContain).start();
            new Customer(synContain).start();
        }
    }
    
    class Producer extends Thread{
        public SynContain synContain;
    
        public Producer(SynContain synContain){
            this.synContain = synContain;
        }
    
        //生產
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    synContain.add(i);
                    System.out.println("生產了第" + i + "只雞");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Customer extends Thread{
        public SynContain synContain;
    
        public Customer(SynContain synContain){
            this.synContain = synContain;
        }
    
        //消費
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    synContain.minus(i);
                    System.out.println("消費了第" + i + "只雞");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Chichen{
        public Chichen(int id) {
            this.id = id;
        }
    
        int id;
    }
    
    class SynContain{
        Chichen[] contain = new Chichen[10];
        int count = -1;
    
        public synchronized void add(int i) throws InterruptedException {
            if(count+1<contain.length){
                contain[++count] = new Chichen(i);
                this.notifyAll();
            }else if (count>=10){
                this.wait();
            }
    
        }
    
        public synchronized void  minus(int i) throws InterruptedException {
            if(count>=0){
                contain[count] = null;
                count--;
                this.notifyAll();
            }else if (count==0){
                this.wait();
            }
        }
    }
    
  • 訊號燈法:設定訊號用於喚醒或等待

  • package com.xiaowei9s.syn;
    
    public class TestPC2 {
        public static void main(String[] args) {
            Food food = new Food();
            new Eater(food).start();
            new Cooker(food).start();
        }
    }
    
    //廚師
    class Cooker extends Thread{
        Food food;
    
        public Cooker(Food food) {
            this.food = food;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                try {
                    food.cook();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    //顧客
    class Eater extends Thread{
        Food food;
    
        public Eater(Food food) {
            this.food = food;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                try {
                    food.eat();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    //菜品
    class Food{
        String name = "滿漢全席";
        boolean flag = false;
    
        public synchronized void eat() throws InterruptedException {
            if (!flag){
                this.wait();
            }
            System.out.println("顧客在吃"+name);
            flag = !flag;
            this.notifyAll();
    
        }
    
        public synchronized void cook() throws InterruptedException {
            if (flag){
                this.wait();
            }
            System.out.println("廚師在做"+name);
            flag = !flag;
            this.notifyAll();
        }
    }
    

十六、使用執行緒池

使用:

demo:

package com.xiaowei9s.syn;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.shutdownNow();
    }
}

class MyThread implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}

知識來源:kuangstudy.com

慢慢來慢慢來