找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 1901|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

JAVA多線程筆記詳解

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
# 多線程詳解

## 核心概念

- 線程就是獨(dú)立的執(zhí)行路徑

- 在程序運(yùn)行時(shí),即使沒(méi)有自己創(chuàng)建線程,后臺(tái)也會(huì)有多個(gè)線程,如主線程, gc線程;

- main()稱之為主線程,為系統(tǒng)的入口,用于執(zhí)行整個(gè)程序;

- 在一個(gè)進(jìn)程中,如果開(kāi)辟了多個(gè)線程,線程的運(yùn)行由調(diào)度器安排調(diào)度,調(diào)度器是與操作系統(tǒng)緊密相關(guān)的,先后順序是不能人為的干預(yù)的。

- 對(duì)同一份資源操作時(shí),會(huì)存在資源搶奪的問(wèn)題,需要加入并發(fā)控制;

- 線程會(huì)帶來(lái)額外的開(kāi)銷,如cpu調(diào)度時(shí)間,并發(fā)控制開(kāi)銷。

- 每個(gè)線程在自己的工作內(nèi)存交互,內(nèi)存控制不當(dāng)會(huì)造成數(shù)據(jù)不一致


## Thread類

**線程開(kāi)啟不一定立即執(zhí)行,由CPU調(diào)度執(zhí)行**

創(chuàng)建一個(gè)新的執(zhí)行線程有兩種方法。

1. 一個(gè)是將一個(gè)類聲明為`Thread`的子類。 這個(gè)子類應(yīng)該重寫`run`類的方法`Thread` 。然后可以分配并啟動(dòng)子類的實(shí)例。

2. 另一種方法來(lái)創(chuàng)建一個(gè)線程是聲明實(shí)現(xiàn)類`Runnable`接口。 那個(gè)類然后實(shí)現(xiàn)了`run`方法。 然后可以分配類的實(shí)例,在創(chuàng)建`Thread`時(shí)作為參數(shù)傳遞,并啟動(dòng)。


然后,以下代碼將創(chuàng)建一個(gè)線程并啟動(dòng)它運(yùn)行:

> ```
>  PrimeRun p = new PrimeRun(143);
>  new Thread(p).start();
> ```

### **方式一**

```java
//創(chuàng)建線程方式一:繼承Thread類,重寫run()方法,調(diào)用start開(kāi)啟線程
public class One extends Thread{
    @Override
    public void run(){
        //run方法線程體
        for (int i = 0; i < 10; i++) {
            System.out.println("數(shù)字---" + i);
        }
    }

    public static void main(String[] args) {
        //main線程,主線程

        //創(chuàng)建一個(gè)線程對(duì)象
        One one = new One();

        //調(diào)用start()方法開(kāi)啟線程
        one.start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("線程---" + i);
        }
    }
}
```

### **方式二**

```java
//創(chuàng)建線程方式二:實(shí)現(xiàn)runnable接口,重寫run方法,執(zhí)行線程需要丟入runnable接口實(shí)現(xiàn)類,調(diào)用start方法。
public class Two implements Runnable{
    @Override
    public void run() {
        //run方法線程體
        for (int i = 0; i < 100; i++) {
            System.out.println("數(shù)字---" + i);
        }
    }

    public static void main(String[] args) {
        //創(chuàng)建runnable接口的實(shí)現(xiàn)類對(duì)象
        Two two = new Two();

        //創(chuàng)建線程對(duì)象,通過(guò)線程對(duì)象來(lái)開(kāi)啟我們的線程,代理
//        Thread thread = new Thread(two);
//        thread.start();

        new Thread(two).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("線程--" + i);
        }
    }

}
```

## 并發(fā)任務(wù)

初識(shí)并發(fā):

```java
public class Ticket implements Runnable{

    //票數(shù)
    private int tickNums = 10;

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

            System.out.println(Thread.currentThread().getName() + "-->拿到了第" + tickNums-- + "張票");
        }
    }

    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(ticket,"阿冰").start();
        new Thread(ticket,"黃牛").start();
        new Thread(ticket,"小澤").start();
    }


}



在運(yùn)行過(guò)程中會(huì)出現(xiàn)多個(gè)線程同時(shí)處理同一個(gè)資源的現(xiàn)象這個(gè)時(shí)候就需要并發(fā)。

**處理并發(fā)任務(wù)的安全性:隊(duì)列+鎖。**

### 龜兔賽跑例程

```java
import java.util.Random;

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

    private static String winner;

    @Override
    public void run() {
        Random random = new Random();
        int time = random.nextInt();

        for (int i = 0; i <= 100; i++) {

            //模擬兔子睡覺(jué)
            if (Thread.currentThread().getName().equals("兔子") && i % time == 0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

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

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

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

    public static void main(String[] args) {
        Race race = new Race();

        new Thread(race,"兔子").start();
        new Thread(race,"烏龜").start();
    }
}
```

## 靜態(tài)代理模式

```java
public class StaticType {
    public static void main(String[] args) {
        WedingCompany wedingCompany = new WedingCompany(new You());
        wedingCompany.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}

//真實(shí)對(duì)象,你去結(jié)婚
class You implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("結(jié)婚了,超級(jí)開(kāi)心!");
    }
}

//代理角色,幫助你結(jié)婚
class WedingCompany implements Marry{

    private Marry target;    //代理誰(shuí)-->真實(shí)目標(biāo)

    public WedingCompany(Marry target){
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }

    private void before() {
        System.out.println("結(jié)婚之前");
    }

    private void after() {
        System.out.println("結(jié)婚之后");
    }

}
```

靜態(tài)代理模式總結(jié):

- 真實(shí)對(duì)象和代理對(duì)象都要實(shí)現(xiàn)同一個(gè)接口

- 代理對(duì)象要代理真實(shí)角色


好處:

- 代理對(duì)象可以做很多真實(shí)對(duì)象做不了的事情

- 真實(shí)對(duì)象專注做自己的事情


## Lambda表達(dá)式

```java
/*
推導(dǎo)Lambda表達(dá)式
*/
public class ClassLambda {

    //3.靜態(tài)內(nèi)部類
    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.局部?jī)?nèi)部類
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("I like lambda3!");
            }
        }
        like = new Like3();
        like.lambda();

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

        //6.用lambda簡(jiǎn)化
        like = ()->{
            System.out.println("I like lambda5!");
        };
        like.lambda();
    }
}

//1.定義一個(gè)函數(shù)式接口
interface ILike{
    void lambda();
}

//2.實(shí)現(xiàn)類
class Like implements ILike {
    @Override
    public void lambda() {
        System.out.println("I like lambda!");
    }
}
```

**簡(jiǎn)化Lambda**

```java
public class TestLambda {
    public static void main(String[] args) {
        //lambda表示簡(jiǎn)化
        Ilove ilove = (int a) -> {
            System.out.println("I love you!" + a);
        };
        //簡(jiǎn)化1.參數(shù)類型
        ilove = (a) -> {
            System.out.println("1.I love you!" + a);
        };
        //簡(jiǎn)化2.簡(jiǎn)化括號(hào)
        ilove = a -> {
            System.out.println("2.I love you!" + a);
        };
        //簡(jiǎn)化3.簡(jiǎn)化花括號(hào)
        ilove = a -> System.out.println("3.I love you!" + a);

        ilove.love(520);
    }
}

interface Ilove{
    void love(int a);
}
```

總結(jié):

- lambda表達(dá)式只能有一行代碼的情況下才能簡(jiǎn)化成為一行,如果有多行,那么就用代碼塊包裹。

- 前提是接口時(shí)函數(shù)式接口。

- 多個(gè)參數(shù)也可以去掉參數(shù)類型,要去掉都去掉,必須加上括號(hào)。


## Stop方法:線程停止

```java
//測(cè)試stop
//1.建議線程正常停止--->利用次數(shù),不建議死循環(huán)
//2.建議使用標(biāo)志位--->設(shè)置一個(gè)標(biāo)志位
//3.不要使用stop或者destroy等過(guò)時(shí)或者JDK不建議使用的方法
public class TestStop implements Runnable {

    //1.設(shè)置一個(gè)標(biāo)志位
    private boolean flag = true;

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

    //2.設(shè)置一個(gè)公開(kāi)的方法停止線程,轉(zhuǎn)換標(biāo)志位
    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("main" + i);
            if (i == 90){
                //調(diào)用stop方法切換標(biāo)志位,讓線程停止
                testStop.stop();
                System.out.println("線程該停止了。");
            }
        }
    }

}
```

## Sleep方法:線程休眠

```java
//計(jì)時(shí)器
public class TimeDown {

    public static void main(String[] args) {
        try {
            Down();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

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

//打印當(dāng)前系統(tǒng)時(shí)間
public class SystemTime {
    public static void main(String[] args) {
        Date startTime = new Date(System.currentTimeMillis());  //獲取當(dāng)前時(shí)間

        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("YYYY/MM/DD\nHH:mm:ss\n").format(startTime));
                startTime = new Date(System.currentTimeMillis());  //更新當(dāng)前時(shí)間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
```

每個(gè)對(duì)象都有一把鎖,sleep方法不會(huì)釋放鎖。

## Yield方法:線程禮讓

- 禮讓線程,讓當(dāng)前正在執(zhí)行的線程暫停,但不阻塞

- 將線程從運(yùn)行態(tài)轉(zhuǎn)化為就緒態(tài)

- 讓CPU重新調(diào)度,禮讓不一定成功,看CPU心情


```java
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() + "-->start");
        Thread.yield();
        System.out.println(Thread.currentThread().getName() + "-->end");
    }
}
```

## Join方法:線程強(qiáng)制執(zhí)行

- Join合并線程,待線程執(zhí)行完成后,在執(zhí)行其他線程,其他線程阻塞

- 不建議使用,容易導(dǎo)致進(jìn)程阻塞


```java
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("VIP插隊(duì)" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //啟動(dòng)我們的線程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主線程
        for (int i = 0; i < 100; i++) {
            if (i == 10){
                thread.join();
            }
            System.out.println("main" + i);
        }
    }
}
```

## 線程的狀態(tài)

線程可以處于以下?tīng)顟B(tài)之一:

- [`NEW`](../../java/lang/Thread.State.html#NEW)  
  尚未啟動(dòng)的線程處于此狀態(tài)。
- [`RUNNABLE`](../../java/lang/Thread.State.html#RUNNABLE)  
  在Java虛擬機(jī)中執(zhí)行的線程處于此狀態(tài)。
- [`BLOCKED`](../../java/lang/Thread.State.html#BLOCKED)  
  被阻塞等待監(jiān)視器鎖定的線程處于此狀態(tài)。
- [`WAITING`](../../java/lang/Thread.State.html#WAITING)  
  正在等待另一個(gè)線程執(zhí)行特定動(dòng)作的線程處于此狀態(tài)。
- [`TIMED_WAITING`](../../java/lang/Thread.State.html#TIMED_WAITING)  
  正在等待另一個(gè)線程執(zhí)行動(dòng)作達(dá)到指定等待時(shí)間的線程處于此狀態(tài)。
- [`TERMINATED`](../../java/lang/Thread.State.html#TERMINATED)  
  已退出的線程處于此狀態(tài)。

**線程狀態(tài)觀測(cè)**

```java
//觀測(cè)線程的狀態(tài)
public class TestState {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("結(jié)束");
        });

        //觀測(cè)狀態(tài)
        Thread.State state = thread.getState();
        System.out.println(state);  //NEW

        //觀察重啟后
        thread.start();
        state = thread.getState();
        System.out.println(state);//RUNNABLE

        while (state != Thread.State.TERMINATED){//只要線程不終止,就一直輸出狀態(tài)
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = thread.getState();  //更新線程狀態(tài)
            System.out.println(state);  //輸出狀態(tài)
        }
    }
}
```

## 守護(hù)優(yōu)先級(jí)

```java
public class TestPriority {
    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);

        t1.start();

        t2.setPriority(2);
        t2.start();

        t3.setPriority(6);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();
    }
}

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

## synchronized鎖機(jī)制

- 由于我們可以通過(guò) private 關(guān)鍵字來(lái)保證數(shù)據(jù)對(duì)象只能被方法訪問(wèn),所以我們只需要針對(duì)方法提出一套機(jī)制,這套機(jī)制就是synchronized關(guān)鍵字,它包括兩種用法:

  - synchronized方法

  - synchronized塊


```java
//同步方法:
public synchronized void method(int args){}    //synchronized默認(rèn)鎖的是this.
```

- synchronized方法控制對(duì)“對(duì)象”的訪問(wèn),每個(gè)對(duì)象對(duì)應(yīng)一把鎖,每個(gè)synchronized方法都必須獲得調(diào)用該方法的對(duì)象的鎖才能執(zhí)行,否則線程會(huì)阻塞,方法一旦執(zhí)行,就獨(dú)占該鎖,直到該方法返回才釋放鎖,后面被阻塞的線程才能獲得這個(gè)鎖,繼續(xù)執(zhí)行

**同步塊**

```java
synchronized (Obj){}
```

- Obj 稱之為同步監(jiān)視器

  - Obj 可以是任何對(duì)象(增刪改的對(duì)象),但是推薦使用共享資源作為同步監(jiān)視器

  - 同步方法中無(wú)需指定同步監(jiān)視器,因?yàn)橥椒椒ǖ耐奖O(jiān)視器就是this ,就是
    這個(gè)對(duì)象本身,或者是class [反射中講解]

- 同步監(jiān)視器的執(zhí)行過(guò)程

  1. 第一個(gè)線程訪問(wèn),鎖定同步監(jiān)視器,執(zhí)行其中代碼.

  2. 第二個(gè)線程訪問(wèn),發(fā)現(xiàn)同步監(jiān)視器被鎖定, 無(wú)法訪問(wèn).

  3. 第一個(gè)線程訪問(wèn)完畢,解鎖同步監(jiān)視器.

  4. 第二個(gè)線程訪問(wèn), 發(fā)現(xiàn)同步監(jiān)視器沒(méi)有鎖,然后鎖定并訪問(wèn)


```java
public class Bank {
    public static void main(String[] args) {
        Account account = new Account(100, "存錢");

        GetMoney You = new GetMoney(account, 50, "我");
        GetMoney Wife = new GetMoney(account, 100, "老婆");

        Wife.start();
        You.start();
    }
}

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

//取錢
class GetMoney extends Thread {
    Account account;    //賬戶
    int TakeMoney;  //取走了多少錢
    int nowMoney;    //手里有多少錢
    public GetMoney(Account account, int TakeMoney, String name){
        super(name);
        this.account = account;
        this.TakeMoney = TakeMoney;
    }

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

        //鎖的對(duì)象是賬戶
        synchronized (account) {
            //判斷有沒(méi)有錢
            if (account.SumMoney - TakeMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "錢不夠,沒(méi)有取到錢");
                return;
            }

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

            //卡內(nèi)余額
            account.SumMoney = account.SumMoney - TakeMoney;
            //你手里的錢
            nowMoney = nowMoney + TakeMoney;

            System.out.println(this.getName() + "手里的錢:" + nowMoney);
            System.out.println(account.name + "余額的錢還有" + account.SumMoney);
        }
    }
}
```

## 死鎖

- 從JDK 5.0開(kāi)始, Java提供了更強(qiáng)大的線程同步機(jī)制一通過(guò)顯式定 義同步鎖對(duì)象來(lái)實(shí)現(xiàn)同步。同步鎖使用Lock對(duì)象充當(dāng)

- java.til.concurrent.locks.Lock接C是控制多個(gè)線程對(duì)共享資源進(jìn)行訪問(wèn)的工具。鎖提供了對(duì)共享資源的獨(dú)占訪問(wèn),每次只能有-個(gè)線程對(duì)Lock對(duì)象加鎖,線程開(kāi)始訪問(wèn)共享資源之前應(yīng)先獲得L ock對(duì)象

- ReentrantLock類實(shí)現(xiàn)了Lock ,它擁有與synchronized相同的并發(fā)性和內(nèi)存語(yǔ)義,在實(shí)現(xiàn)線程安全的控制中,比較常用的是ReentrantLock,可以顯式加鎖、釋放鎖。


```java
import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        wantTicket buy = new wantTicket();
        new Thread(buy,"a").start();
        new Thread(buy,"b").start();
        new Thread(buy,"c").start();
    }
}

class wantTicket implements Runnable{

    int TicketNum = 10;

    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {//上鎖
                lock.lock();
                if (TicketNum > 0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "-->拿到了第" + TicketNum-- + "張票");
                }else {
                    break;
                }

            }finally {
                lock.unlock();
            }
        }
    }
}
```

- Lock是顯式鎖(手動(dòng)開(kāi)啟和關(guān)閉鎖,別忘記關(guān)閉鎖) synchronized是隱式鎖, 出了作用域自動(dòng)釋放

- Lock只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖

- 使用Lock鎖,JVM將花費(fèi)較少的時(shí)間來(lái)調(diào)度線程,性能更好。并且具有更好的擴(kuò)展性(提供更多的子類)

- 優(yōu)先使用順序:

  - Lock >同步代碼塊(已經(jīng)進(jìn)入了方法體,分配了相應(yīng)資源) >同步方法(在方法體之外)

### 生產(chǎn)者消費(fèi)者問(wèn)題

| 方法名 | 作用  |
| --- | --- |
| wait() | 表示線程一直等待 ,直到其他線程通知,與sleep不同,會(huì)釋放鎖 |
| wait(long timeout) | 指定等待的毫秒數(shù) |
| notify | 喚醒一個(gè)處于等待狀態(tài)的線程 |
| notifyAll | 喚醒同一個(gè)對(duì)象上所有調(diào)用wait()方法的線程,優(yōu)先級(jí)別高的線程優(yōu)先調(diào)度 |

```java
//測(cè)試:生產(chǎn)者消費(fèi)者模型-->利用緩沖區(qū):管程法

//生產(chǎn)者,消費(fèi)者,產(chǎn)品,緩沖區(qū)
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}

//生產(chǎn)者
class Productor extends Thread{
    SynContainer container;
    public Productor(SynContainer container){
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生產(chǎn)了" +i +"只雞");
        }
    }
}

//消費(fè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++) {
            System.out.println("消費(fèi)了-->" + container.pop().id + "只雞");
        }
    }
}

//產(chǎn)品
class Chicken{
    int id;
    public Chicken(int id) {
        this.id = id;
    }
}

//緩沖區(qū)
class SynContainer{
    //需要一個(gè)容器大小
    Chicken[] chickens= new Chicken[10];
    //容器計(jì)數(shù)器
    int count = 0;

    //生產(chǎn)者生產(chǎn)產(chǎn)品
    public synchronized void push(Chicken chicken){
        //如果容器滿了,我們就需要等待消費(fèi)
        if (count == chickens.length){
            //通知消費(fèi)者消費(fèi),生產(chǎn)等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {

            //如果沒(méi)有滿,我們就需要丟入產(chǎn)品
            chickens[count] = chicken;
            count++;

            //可以通知消費(fèi)者消費(fèi)了
            this.notifyAll();
        }

    }

    //消費(fèi)者消費(fèi)產(chǎn)品
    public synchronized Chicken pop(){
        //判斷能否消費(fèi)
        if (count == 0){
            //等待生產(chǎn)者生產(chǎn),消費(fèi)者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

            //如果可以消費(fèi)
            count--;
            Chicken chicken = chickens[count];

            //吃完了,通知生產(chǎn)者生產(chǎn)
            this.notifyAll();

            return chicken;
    }

}
```

```java
//測(cè)試生產(chǎn)者消費(fèi)者問(wèn)題2-->信號(hào)燈法,標(biāo)志位解決
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生產(chǎn)者-->演員
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("河南衛(wèi)視");
            }else {
                this.tv.play("春節(jié)聯(lián)歡晚會(huì)");
            }
        }
    }
}

//消費(fèi)者-->觀眾
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();
        }
    }
}

//產(chǎn)品-->節(jié)目
class TV {
    //演員表演,觀眾等待 T
    //觀眾觀看,演員等待 F
    String voice; //表演的節(jié)目
    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;
    }
}
```

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表