Java多线程-04-线程协作

目录

一.线程简介

二.线程创建

三.线程状态

四.线程同步

五.线程协作

五.线程协作

生产者消费者问题

为什么需要线程通信:为了解决生产者和消费者问题

image-20220418103613289


问题分析

image-20220418103751889


解决线程通信的方法

image-20220418104111940


解决方式1:管程法

image-20220418104513590


解决方式2:信号灯法

通过一个标志位来判断

image-20220418104823470


管程法

例子:

生产者:厨师制作炸鸡
消费者:吃炸鸡
缓冲区:取餐台(容器,用来放炸鸡)

如果不使用管程法,就只能让厨师先做完100只炸鸡,然后顾客吃掉100只炸鸡,这显然不合理

  • wait() 会让当前持有对象锁的线程等待并释放锁

​ A.wait() 会让当前持有A对象锁的线程等待并释放锁

  • notifyAll() 唤醒正在等待此对象锁的所有线程

    ​ A.notifyAll() 唤醒正在等待A对象锁的所有线程

我说:

1.谁来调用两个方法?

​ 被消费者和生产者操作的对象,即缓冲区

2.生产者何时等待何时被唤醒?

​ 缓冲区满时生产者自行等待

​ 生产者消费后唤醒生产者

3.消费者何时等待何时被唤醒?

​ 缓冲区空时消费者自行等待

​ 生产者生产后唤醒消费者

package 四线程协作;

//生产者和消费者问题:利用缓冲区解决(管程法)
//例子
//生产者:厨师制作炸鸡
//消费者:吃炸鸡
//缓冲区:取餐台(容器,用来放炸鸡)
public class MonitorMethod {
    public static void main(String[] args) {
        PC_Container container = new PC_Container();

        new Productor("|厨师|", container).start();
        new Consumer("||顾客||", container).start();
    }
}

//生产者线程:厨师制作炸鸡
class Productor extends Thread{
    public PC_Container container;

    public Productor(String name, PC_Container container){
        super(name);
        this.container = container;
    }

    @Override
    public void run() {
        //一共制作100只炸鸡
        for (int i = 1; i <= 100; i++) {
            //制作一只炸鸡并放到前台
            container.push(new Chicken(i));
            System.out.println(this.getName() + "-->生产了第"+ i + "只炸鸡");
        }
    }
}

//消费者线程:吃炸鸡
class Consumer extends Thread{
    public PC_Container container;

    public Consumer(String name, PC_Container container){
        super(name);
        this.container = container;
    }

    @Override
    public void run() {
        //一共吃100只炸鸡
        for (int i = 1; i <= 100; i++) {
            //从前台取一只炸鸡
            Chicken chicken = container.pop();
            System.out.println(this.getName() + "-->消费了第"+ chicken.id + "只炸鸡");
        }
    }
}

//炸鸡
class Chicken {
    //炸鸡编号
    public int id;

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

//缓冲区:取餐台(容器,用来放炸鸡)
//会被生产者和消费者修改(多个线程操作同一个对象)
class PC_Container {
    //容器大小:前台最多能放10只炸鸡
    public Chicken[] chickens = new Chicken[10];
    //容器计数器
    public int count = 0;

    //放入炸鸡
    //该方法涉及修改PC_Container,使用同步方法(监视器是this),即锁PC_Container
    public synchronized void push(Chicken chicken){
        //容器满了-前台已经放满10只炸鸡了
        if(count == chickens.length){
            //让生产者等待
            try {
                //wait()会让当前持有PC_Container对象锁的线程等待并释放锁
                //持有锁并且跑push方法的只能是生产者线程,所以这里的this.wait()会让生产者线程等待
                this.wait();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果可以生产
        //这里不需要写else if,因为如果生产者处于等待,那么下面的代码就不会执行了
        //没有满,丢入炸鸡
        chickens[count] = chicken;
        count++;

        //现在前台有炸鸡了,需要唤醒消费者
        //notifyAll()唤醒正在等待此对象监视器锁的所有线程
        //notifyAll()可以唤醒生产者和消费者,但此时生产者本来就是醒,所以旨在唤醒消费者来吃炸鸡
        this.notifyAll();
    }

    //放入炸鸡
    //该方法涉及修改PC_Container,使用同步方法(监视器是this),即锁PC_Container
    public synchronized Chicken pop(){
        //容器空了-前台没有炸鸡了
        if(count == 0){
            //让消费者等待
            try {
                //跑pop方法的只能是消费者线程,所以这里的this.wait()会让消费者线程等待
                this.wait();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果可以消费
        //取走炸鸡
        count--;//注意这里要先减,count下标指向的是数组中下一个空白的位置,count-1才是最新放入的炸鸡
        Chicken chicken = chickens[count];

        //因为消费者取走了炸鸡所以现在前台一定不是满的
        //需要通知生产者生产
        this.notifyAll();

        return chicken;
    }
}
image-20220418145642592

信号灯法

例子

生产者:演员表演节目
消费者:观众收看节目

演员和生产者都操作同一个电视机

演员通过 电视机上的信号灯 判断自己的状态

1.等待

2.表演节目,之后提醒观众观看

观众通过 电视机上的信号灯 判断自己的状态

1.等待

2.收看节目,之后提醒演员表演

package 四线程协作.生产者消费者问题;

import java.security.PublicKey;

//生产者和消费者问题:利用标志位解决(信号灯法)
//例子
//生产者:演员表演节目
//消费者:观众收看节目
public class SignalLight {
    public static void main(String[] args) {
        TV tv = new TV();

        new PLayer(tv).start();
        new Watcher(tv).start();
    }
}

//生产者:演员
class PLayer extends Thread{
    public TV tv;
    public PLayer(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if(i%2==0)      this.tv.play("地球脉动");
            else            this.tv.play("COSMOS");
        }
    }
}

//消费者:观众
class Watcher extends Thread{
    public TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }

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

//电视机TV
class TV {
    //节目名字
    private String programName;

    //信号灯标志位
    //演员表演中,观众等待 ture
    //观众收看中,演员等待 false
    private boolean flag = true;

    //演员表演节目
    public synchronized void play(String programName){
        if(!flag){
            try {
                //观众收看中 演员等待
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //观众等待中 演员表演
        this.programName = programName;
        System.out.println("演员表演了-->" + programName);

        //演员表演完了
        this.flag = !this.flag;//信号灯反转
        //通知唤醒观众观看
        this.notifyAll();
    }

    //观众收看节目
    public synchronized void watch(){
        if(flag){
            try {
                //演员表演中 观众等待
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //演员等待中 观众收看
        System.out.println("观众收看了-->" + this.programName);

        //观众收看完了
        this.flag = !this.flag;//信号灯反转
        //通知唤醒演员表演
        this.notifyAll();
    }
}
image-20220418155833074

线程池

image-20220418170835845


image-20220418172826759


public class Pool_Test {
    public static void main(String[] args) {
        //1.创建服务,创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);//参数为线程池大小

        //执行
        service.execute(new MyThead());
        service.execute(new MyThead());
        service.execute(new MyThead());
        service.execute(new MyThead());

        //2.关闭连接
        service.shutdown();
    }
}

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

posted @ 2022-04-18 18:01  Cornfield_Chase  阅读(70)  评论(0)    收藏  举报