java多線程筆記 b站狂神視頻筆記


線程創建

Thread、Runnable、Callable

繼承Thread類實現Runnable接口 為重點,實現Callable接口僅作了解

Thread

  • 自定義線程類繼承Thread類
  • 重寫run()方法,編寫線程執行體
  • 創建線程對象,調用start()方法啟動線程
package com.company.dxc;

// 創建線程的方式:繼承Thread類 、重寫run()方法、 調用start開啟線程

// 總結:線程開啟不一定立即執行,由cpu進行調度執行

public class TestThread01 extends Thread {
    @Override
    public void run() {
        // run方法線程體
        for (int i = 0; i < 200; i++) {
            System.out.println("正在執行線程----" + i);
        }
    }

    public static void main(String[] args) {
        // main主線程
        // 創建一個線程對象
        TestThread01 testThread01 = new TestThread01();
        //調用start()方法開啟線程
        testThread01.start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("正在執行主方法******" + i);
        }
    }
}

Runnable

  • 定義MyRunnable類實現Runnable接口
  • 實現run()方法,編寫線程執行體
  • 創建線程對象,調用start()方法啟動線程
package com.company.dxc;
// 創建線程方式2
// 實現Runnable接口,重寫run方法,執行線程需要丟入Runnable接口實現類 調用start執行
public class TestThread02 implements Runnable {
    @Override
    public void run() {
        // run方法線程體
        for (int i = 0; i < 200; i++) {
            System.out.println("正在執行線程----" + i);
        }
    }

    public static void main(String[] args) {
        // main主線程
        // 創建Runnable接口的實現類對象
        TestThread01 testThread01 = new TestThread01();
        //  創建線程對象,通過線程對象來開啟我們的線程,代理
        // Thread tr = new Thread(testThread01);
        // tr.start();

        new Thread(testThread01).start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("正在執行主方法******" + i);
        }
    }
}

Thread與Runnable比較

繼承Thread類 實現Runnable接口
子類繼承Thread類具備多線程能力 實現接口Runnable具有多線程能力
啟動線程:子類對象.start(); 啟動線程:傳入目標對象+Thread對象.start();
不建議使用:避免OOP單繼承局限性 推薦使用:避免單繼承局限性,靈活方便,方便同一個對象被多個線程使用

例子1. 購買火車票

通過本例子發現並發問題

package com.company.dxc;
// 多個線程同時操作同一個對象
// 買火車票的例子

// 問題:多個線程同時對一個資源進行操作 解決並發問題
public class TestThread03 implements Runnable {

    // 票數
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true){
            if(ticketNums <= 0) break;
            // 模擬演示
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + " --> 拿到了NO:" + ticketNums-- + "票" );
        }
    }

    public static void main(String[] args) {
        TestThread03 ts = new TestThread03();
        new Thread(ts, "stu").start();
        new Thread(ts, "teacher").start();
        new Thread(ts, "gangster").start();
    }
}

例子2. 龜兔賽跑

package com.company.dxc;

// 模擬龜兔賽跑
public class race implements Runnable {

    // 勝利者
    private static String winner;

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {

            // 模擬兔子休息
            if (Thread.currentThread().getName().equals("rabbit") && i%10 == 0){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // 判斷比賽是否結束
            boolean flag = gameOver(i);
            // 如果比賽結束 停止程序
            if (flag) {break;}

            System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "steps");
        }
    }

    //判斷是否完成比賽
    private boolean gameOver(int steps) {
        // 判斷是否有勝利者
        if (winner != null) return true; // 已經存在勝利者
        {
            if (steps >= 100) {
                winner = Thread.currentThread().getName();
                System.out.println("winner is " + winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        race r1 = new race();
        new Thread(r1, "rabbit").start();
        new Thread(r1, "tortoise").start();

    }
}

Callable(僅了解)

  1. 實現Callable接口,需要返回值類型
  2. 重寫call方法,需要拋出異常
  3. 創建目標對象
  4. 創建執行服務 ExecutorService ser = Executors.newFixedThreadPood(1);
  5. 提交執行 Future<Boolean> result = ser.submit(t1);
  6. 獲取結果 boolean r1 = result.get();
  7. 關閉服務 ser.shutdownNow();

靜態代理

個人結婚與婚慶公司的例子

package com.company.dxc;

// 靜態代理模式:
// 真實對象和代理對象都要實現同一個接口
// 代理對象要代理真實角色

// 好處 :
// 1. 代理對象可以做很多真實對象做不了的事情
// 2. 真實對象專注做自己的事情


public class StaticProxy {
    public static void main(String[] args) {
        You you = new You(); // 真實對象
        new Thread(() -> System.out.println("i love you")).start(); // lambda表達式
        new WeddingCompany(new You()).HappyMarry();
//        WeddingCompany weddingCompany = new WeddingCompany(you);
//        weddingCompany.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}

// 真實角色 去結婚
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("大喜之日結婚了!");
    }
}

// 代理角色 起到幫助作用
class WeddingCompany implements Marry{

    // 代理誰 --> 真實目標角色
    private Marry target;
    public WeddingCompany(Marry target){
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry(); // 這是真實對象
        after();
    }

    private void after() {
        System.out.println("結婚之后,數錢");
    }

    private void before() {
        System.out.println("結婚之前,布置場所");
    }
}

Lambda表達式

為什么要使用lambda表達式

  • 避免匿名內部類定義過多
  • 可以讓代碼看起來更簡潔
  • 去掉無意義代碼,留下核心邏輯

注:只有一行代碼的情況下才能簡化成一行;前提是接口為函數式接口

package com.company.dxc;
/*
* 推導lambda表達式
*/
public class TestLambda {

    // 3. 靜態內部類
    static class Like2 implements Ilike{

        @Override
        public void lambda() {
            System.out.println("i like lambda2!");
        }
    }

    public static void main(String[] args) {
        Ilike like = new Like();
        like.lambda();

        like = new Like2();
        like.lambda();

        // 4. 局部內部類
        class Like3 implements Ilike{

            @Override
            public void lambda() {
                System.out.println("i like lambda3!");
            }
        }
        like = new Like3();
        like.lambda();

        // 5. 匿名內部類,沒有類的名稱,必須借助接口或者父類
        like = new Ilike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda4");
            }
        };
        like.lambda();

        // 6. 用lambda表達式
        like = () -> {
            System.out.println("i like lambda5");
        };
        like.lambda();

    }
}

// 1. 定義一個函數式接口
interface Ilike{
    void lambda();
}

// 2. 實現類
class Like implements Ilike{
    @Override
    public void lambda() {
        System.out.println("i like lambda!");
    }
}

線程狀態

創建狀態、阻塞狀態、死亡裝填、就緒狀態運行狀態

image-20210201162717555

線程的方法

image-20210201163727816

停止線程

  • 不推薦使用JDK提供的stop(); destroy();方法
  • 推薦線程自己停止
  • 建議使用一個標志位進行終止變量
package com.company.dxc;

public class TestStop implements Runnable {
    // 1. 設置標志位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println("Thread is running...." + i++);
        }
    }

    // 2. 設置一個公開的方法停止線程,轉換標志位

    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        // main 先執行
        for (int i = 0; i < 1000; i++) {
            System.out.println("main is running..." + i);
            if(i == 900){
                //調用stop方法停止線程
                testStop.stop();
                System.out.println("Thread stop!");
            }
        }
    }

}

線程休眠

  • sleep(時間)指定當前線程阻塞的毫秒數
  • sleep存在異常InterruptedException
  • sleep時間到達后線程進入就緒狀態
  • sleep可以模擬網絡延時,倒計時等
  • 每一個對象都有一個鎖,sleep不會釋放鎖
package com.company.dxc;

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

public class TestSleep {
    public static void main(String[] args) throws InterruptedException {
      //  down();
        // 打印當前系統時間
        Date date = new Date(System.currentTimeMillis());
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
                date = new Date(System.currentTimeMillis()); // 更新當前時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void down() throws InterruptedException {
        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num --);
            if(num <= 0) break;
        }
    }
}

線程禮讓

  • 禮讓線程,讓當前正在執行的線程暫停,但不阻塞
  • 將線程從運行狀態轉為就緒狀態
  • 讓CPU重新調度,禮讓不一定成功,看CPU心情
package com.company.dxc;
// 測試不一定成功的線程禮讓

public class TestYield {
    public static void main(String[] args) {
        myYield myYield = new myYield();
        new Thread(myYield, "a").start();
        new Thread(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()+"線程停止執行");
    }
}

Join

  • join合並線程,待此線程執行完成后再執行其他線程,其他線程阻塞
  • 可以想象成插隊
package com.company.dxc;

public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("★ VIP Thread ☆" + 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 < 500; i++) {
            if(i == 200){
                thread.join(); // 插隊
            }
            System.out.println("main" + i);
        }
    }
}

線程優先級

  • java提供一個線程調度器來監控程序中啟動后進入就緒狀態的所有線程,調度器按照優先級決定應該調度哪個線程來執行
  • 線程的優先級用數字表示,范圍從1~10 Thread.MIN_PRIORITY = 1;
  • 使用以下方式改變或獲取優先級 getPriority().setPriority(int xxx);

優先級低只是意味着獲得調度的概率低,並不是高優先級必然先調用 (性能倒置問題)

package com.company.dxc;

public class TestPriority extends Thread {
    public static void main(String[] args) {
        // 主線程優先級
        System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());

        myPriority myPriority = new myPriority();

        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);

        // 先設置優先級 再啟動
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY); // Max 為最大 10
        t4.start();

        t5.setPriority(Thread.MIN_PRIORITY); // min 為最小 1
        t5.start();

    }
}

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

守護線程(daemon)

  • 線程分為用戶線程和守護線程
  • 虛擬機必須確保用戶線程執行完畢
  • 虛擬機不用等待守護線程執行完畢
  • 如:后台記錄操作日志、監控內存、垃圾回收 etc.
package com.company.dxc;
// 測試守護線程 上帝守護人類為例

public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        Human human = new Human();
        Thread thread = new Thread(god);
        thread.setDaemon(true); // 默認false表示用戶線程,正常線程都是用戶線程

        thread.start();// 守護線程啟動
        new Thread(human).start(); // 用戶線程啟動

    }
}

// 上帝
class God implements Runnable{

    @Override
    public void run() {
        while(true){ // 按理來說不會結束 但作為守護線程在用戶線程結束后 隨之結束(可能會伴隨虛擬機關閉的一點點延遲)
            System.out.println("正在守護!");
        }
    }
}

// 人類
class Human implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("每天都要開心活着!");
        }
        System.out.println("goodbye world!");
    }
}

線程同步

並發:同一個對象被多個線程同時操作

處理多線程問題時,多線程訪問一個對象,並且某些線程還想修改這個對象,這時候就需要線程同步。線程同步是一種等待機制,多個需要同時訪問此對象的線程進入這個對象的等待池形成隊列,等待前面線程使用完畢再讓下一個線程使用

隊列與鎖

兩個不安全線程程序的例子

1. 不安全購票

package com.company.dxc;

import javax.swing.plaf.TableHeaderUI;

//不安全買票
// 線程不安全,有負數
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station, "買票人老壹 ").start();
        new Thread(station, "買票人老貳 ").start();
        new Thread(station, "黃牛 ").start();
    }
}

class BuyTicket implements Runnable{
    // 票
    private int ticketNum = 10;
    boolean flag = true; // 外部停止方式
    @Override
    public void run() {
        // 買票
        while(true){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void buy() throws InterruptedException {
        // 判斷是否有票
        if(ticketNum <= 0){
            flag = false;
            return;
        }

        //模擬延時
        Thread.sleep(100);

        // 買票
        System.out.println(Thread.currentThread().getName() + "買到了" + ticketNum--);
    }
}

2. 不安全取款

package com.company.dxc;

// 不安全取錢
// 兩個人去取錢
public class UnsafeBank {
    public static void main(String[] args) {
        // 賬戶
        Account account = new Account(100, "存款金額");
        Drawing you = new Drawing(account, 50, "你");
        Drawing gf = new Drawing(account, 100, "對方");
        you.start();
        gf.start();
    }
}

// 賬戶
class Account{
    int money; // 余額
    String name; // 卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}
// 銀行 模擬取款
class Drawing extends Thread{
    Account account; // 賬戶
    int drawingMoney; // 取了多少錢
    int nowMoney; // 還剩多少錢

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

    //取錢操作
    @Override
    public void run() {
        // 判斷有沒有錢
        if(account.money - drawingMoney < 0){
            System.out.println(Thread.currentThread().getName() + "錢不夠,取不了咯!");
            return;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 卡內余額 = 余額 - 你取的錢
        account.money = account.money - drawingMoney;
        // 你手里的錢
        nowMoney = nowMoney + drawingMoney;

        System.out.println(account.name + "余額為:" + account.money);
        // 此時 Thread.currentThread().getName() = this.getName()
        System.out.println(this.getName() + "手里的錢:" + nowMoney);
    }
}

同步方法

  • 由於我們可以通過private關鍵字來保證數據對象只能被方法訪問,所以我們只需要針對方法提出一套機制,這套機制就是 synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized

    同步方法 public synchronized void method(int args){ }

  • synchronized方法控制對“對象”的訪問,每個對象對應一把鎖,每個synchronized方法都必須獲得調用該方法的對象的鎖才能執行,否則線程會阻塞,方法一旦執行,就獨占該鎖,直到該方法返回才釋放鎖,后面被阻塞的線程才能獲得這個鎖,繼續執行

package com.company.dxc;

import javax.swing.plaf.TableHeaderUI;

//不安全買票
// 線程不安全,有負數
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station, "買票人老壹 ").start();
        new Thread(station, "買票人老貳 ").start();
        new Thread(station, "黃牛 ").start();
    }
}

class BuyTicket implements Runnable{
    // 票
    private int ticketNum = 10;
    boolean flag = true; // 外部停止方式
    @Override
    public void run() {
        // 買票
        while(true){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
		// *********此處進行同步方法************
    // synchronized 同步方法,鎖的是this
    private synchronized void buy() throws InterruptedException {
        // 判斷是否有票
        if(ticketNum <= 0){
            flag = false;
            return;
        }

        //模擬延時
        Thread.sleep(100);

        // 買票
        System.out.println(Thread.currentThread().getName() + "買到了" + ticketNum--);
    }
}
package com.company.dxc;

// 不安全取錢
// 兩個人去取錢
public class UnsafeBank {
    public static void main(String[] args) {
        // 賬戶
        Account account = new Account(100, "存款金額");
        Drawing you = new Drawing(account, 50, "你");
        Drawing gf = new Drawing(account, 100, "對方");
        you.start();
        gf.start();
    }
}

// 賬戶
class Account{
    int money; // 余額
    String name; // 卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}
// 銀行 模擬取款
class Drawing extends Thread{
    Account account; // 賬戶
    int drawingMoney; // 取了多少錢
    int nowMoney; // 還剩多少錢

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

    //取錢操作
    @Override
    public void run() {
      	// ----------此時進行同步操作--------------
        // synchronized 默認鎖的是this,所以此時用同步塊對account進行同步
        synchronized (account){
            // 判斷有沒有錢
            if(account.money - drawingMoney < 0){
                System.out.println(Thread.currentThread().getName() + "錢不夠,取不了咯!");
                return;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 卡內余額 = 余額 - 你取的錢
            account.money = account.money - drawingMoney;
            // 你手里的錢
            nowMoney = nowMoney + drawingMoney;

            System.out.println(account.name + "余額為:" + account.money);
            // 此時 Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "手里的錢:" + nowMoney);
        }
    }
}

死鎖

產生死鎖的四個必要條件

  1. 互斥:一個資源每次只能被一個進程使用
  2. 請求與保持:一個進程因請求資源而阻塞時,對已獲得的資源保持不放
  3. 不剝奪:進程已獲得的資源,在未用完之前,不能強行剝奪
  4. 循環等待:若干進程之間形成一種頭尾相接的循環等待資源關系
package com.company.dxc;
// 死鎖:多個線程互相擁有對方需要的資源,形成僵持
public class DeadLock {
    public static void main(String[] args) {
        Makeup moore = new Makeup(0, "Moore");
        Makeup dove = new Makeup(0, "Dove");
        moore.start();
        dove.start();
    }
}

class Lipstick{
}
class Mirror{
}

class Makeup extends Thread{

    // 需要的資源只有一份,用static來保證只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice; // 選擇
    String girlName; // 使用化妝品的人

    Makeup(int choice, String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }
    @Override
    public void run() {
        // 化妝
        try {
            Makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    // 化妝 互相持有對方的鎖,就是需要拿到對方的資源
    private void Makeup() throws InterruptedException {
        if(choice == 0){
            synchronized (lipstick){
                // 獲得口紅的鎖
                System.out.println(this.girlName + "獲得口紅的鎖");
                Thread.sleep(1000);
            }
            synchronized (mirror){
                // 獲得鏡子的鎖
                System.out.println(this.girlName + "獲得鏡子的鎖");
            }
        }else{
            synchronized (mirror){
                // 獲得口紅的鎖
                System.out.println(this.girlName + "獲得鏡子的鎖");
                Thread.sleep(2000);
            }
            synchronized (lipstick){
                // 獲得鏡子的鎖
                System.out.println(this.girlName + "獲得口紅的鎖");
            }
        }
    }
}

Lock 鎖

class A{
  private final ReentrantLock lock = new ReenTrantLock();
  public void m(){
    lock.lock();
    try{
      // 保證線程安全的代碼
    }
    finally{
			lock.unlock();
      // 如果同步代碼有異常,要將unlock()寫入finally語句塊 
    }
  }
}

synchronized 與 lock 的對比

  • Lock是顯示鎖,需要手動開啟和關閉,synchronized為隱式鎖,出了作用域自動釋放
  • lock只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖
  • 使用lock鎖,jvm將花費較少的時間來調度線程,性能更好。並且具有更好的擴展性
  • 優先使用順序
    • Lock > 同步代碼快(已經進入方法體,分配了相應資源)> 同步方法(在方法體之外)

線程協作 - 生產者消費者問題

線程同步問題,生產者和消費者共享同一個資源,並且生產者和消費者之間相互依賴,互為條件

  • 對於生產者,沒有生產產品之前,要通知消費者等待,而生產了產品之后,有需要馬上通知消費者消費
  • 對於消費者,在消費之后,要通知生產者已經結束消費,需要生產新的產品以供消費
  • 在生產者消費者問題中,僅有synchronized是不夠的
    • synchronized可組織並發更新同一個共享資源,實現了同步
    • synchronized不能用來實現不同線程之間的消息傳遞(通信)

Java提供了幾個方法解決線程之間的通信問題

方法名 作用
wait() 表示線程一直鄧艾,直到其他線程通知,與sleep()不同,會釋放鎖
wait(long timeout) 指定等待的毫秒數
notify() 喚醒一個處於等待狀態的線程
notifyAll() 喚醒同一個對象上所有調用wait()方法的線程,優先級別高的線程優先調度

注意: 均是Object類的方法,都只能在同步方法或者同步代碼快中使用,否則會拋出異常 IllegalMonitorStateException

管程法

  • 生產者:負責生產數據的模塊(可能是方法、對象、線程、進程)
  • 消費者:負責處理數據的模塊(可能是方法、對象、線程、進程)
  • 緩沖區:消費者不能直接使用生產者的數據,利用中間“緩沖區”
package com.company.dxc;

// 測試 生產者消費者模型 --> 利用緩沖區解決:管程法

public class TestPC {
    public static void main(String[] args) {
        SynBuffer synBuffer = new SynBuffer();

        new Producer(synBuffer).start();
        new Consumer(synBuffer).start();
    }
}

// 生產者
class Producer extends Thread{
    SynBuffer buffer;
    public Producer(SynBuffer buffer){
        this.buffer = buffer;
    }
    // 生產

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生產了" + i +"只雞");
            try {
                buffer.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 消費者
class Consumer extends Thread{
    SynBuffer buffer;
    public Consumer(SynBuffer buffer){
        this.buffer = buffer;
    }

    // 消費

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                System.out.println("消費了-->" + buffer.pop().id +"只雞");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 產品
class Chicken{
    int id; // 產品編號

    public Chicken(int id) {
        this.id = id;
    }
}

// 緩沖區
class SynBuffer{

    //容器大小
    Chicken[] chickens = new Chicken[10];
    // 容器計數器
    int count = 0;

    // 生產者放入產品
    public synchronized void push(Chicken chicken) throws InterruptedException {
        // 如果容器滿了,需要等待消費者消費
        if(count == chickens.length){
            // 通知消費者消費,生產等待
            this.wait();
        }
        // 如果沒有滿,需要丟入產品
        chickens[count] = chicken;
        count ++;
        // 可以通知消費者消費了
        this.notifyAll();
    }

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

        return chicken;
    }
}

信號燈法

package com.company.dxc;

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;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0){
                this.tv.play("節目一:新聞聯播");
            }else{
                this.tv.play("節目二:法治在線");
            }
        }
    }
}
// 消費者 --> 觀眾
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

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

        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("演員表演了:" + voice);
        // 通知觀眾觀看
        this.notifyAll(); // 通知喚醒
        this.voice = voice;
        this.flag = !this.flag;
    }
    // 觀看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("觀看了:" + voice);
        // 通知演員表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

線程池

image-20210204153459575

使用線程池

image-20210204153549425

package com.company.dxc;

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

public class TestPool {
    public static void main(String[] args) {
        // 1. 創建服務,創建線程池
        // newFixedThreadPool 參數為線程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);
        // 執行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        // 2. 關閉連接
        service.shutdown();
    }
}

class   MyThread implements Runnable{

    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}

總結

package com.company.dxc;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class summary {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        new MyThread1().start();

        new Thread(new MyThread2()).start();

        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
        new Thread(futureTask).start();
        Integer integer = futureTask.get();
        System.out.println(integer);
    }
}

// 1. 繼承Thread類
class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("My Thread1");
    }
}
// 2. 實現Runnable接口
class MyThread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("My Thread2");
    }
}
// 3. 實現Callable接口
class MyThread3 implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("My Thread3");
        return 100;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM