多线程04—浅谈线程通信

时间:2020-09-19 00:41:07   收藏:0   阅读:58

1. 应用场景:生产者和消费者问题

2. 问题分析

这是一个线程同步问题 , 生产者和消费者共享同一个资源 , 并且生产者和消费者之间相互依赖 , 互为条件。

3. Java中涉及到线程通信的方法

方法 作用
wait() 线程会处于等待状态,直到其他线程通知,这个方法会释放锁
wait(long timeout) 指定等待的毫秒
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程 , 优先级别高的线程优先调度

4. 管程法

(1)Productor.java——生产者

package com.pbx.lesson03;

/**
 * 生产者
 * @author BruceXu
 * @date 2020/9/17
 */
public class Productor extends Thread {
    private Buffer buffer;
    public Productor(Buffer buffer){
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了第"+i+"只鸡");
            buffer.push(new Chicken(i));
            // 可以通过修改sleep的时间来模拟不同的生产速度
            try {
                if (i<10) {
                    Thread.sleep(100);
                } else {
                    Thread.sleep(200);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(2)Customer.java——消费者

package com.pbx.lesson03;

/**
 * 消费者
 * @author BruceXu
 * @date 2020/9/17
 */
public class Customer extends Thread {
    private Buffer buffer;

    public Customer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Chicken chicken = buffer.pop();
            System.out.println("消费了第"+chicken.getId()+"只鸡,现在还有"+buffer.getCount()+"只鸡剩余");
            try {
                // 可以通过修改sleep的时间来模拟不同的消费
                Thread.sleep(150);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(3)Buffer.java——缓冲区

package com.pbx.lesson03;

/**
 * 缓冲区
 * @author BruceXu
 * @date 2020/9/17
 */
public class Buffer {
    // 需要一个容器来存放商品
    private Chicken[] chickens = new Chicken[10];
    // 需要一个计数器
    private int count = 0;

    public int getCount() {
        return count;
    }

    // 生产者要放入商品
    public synchronized void push(Chicken chicken) {
        // 如果容器满了,生产者等待,通知消费者消费
        if(count >= chickens.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果没满,就可以继续放入商品
        chickens[count] = chicken;
        count++;

        //  通知消费者消费
        this.notifyAll();
    }

    // 消费者要消费商品
    public synchronized Chicken pop(){
        // 判断能不能消费
        if (count==0) {
            // 等待生产,等待消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 消费
        count--;
        this.notifyAll();
        return chickens[count];
    }
}

(4)Chicken.java——商品

package com.pbx.lesson03;

/**
 * 商品
 *
 * @author BruceXu
 * @date 2020/9/17
 */
public class Chicken {
    private int id;

    public int getId() {
        return id;
    }

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

(5)Main.java

package com.pbx.lesson03;

/**
 * @author BruceXu
 * @date 2020/9/17
 */
public class Main {
    public static void main(String[] args) {
        Buffer buffer = new Buffer();
        new Productor(buffer).start();
        new Customer(buffer).start();
    }
}

技术分享图片

5. 信号灯法

(1)TV.java

package com.pbx.lesson04;

/**
 * 看电视
 * 节目时间广告等待播放,广告时间节目等待播放
 *
 * @author BruceXu
 * @date 2020/9/18
 */
public class TV {
    // 广告时间,false;节目时间,ture
    private boolean flag;

    public synchronized void playShow(String name) {
        // 播放节目之前,先看广告有没有播放过
        if (!flag) {
            // 没放过广告,那么等待广告播放
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("播放节目:" + name);
        // 更改标志位
        this.flag = !this.flag;
        // 节目放过了,该放广告了
        this.notifyAll();
    }

    public synchronized void playAD(String name) {
        // 节目放完了吗?如果没有,就等待节目放完
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("播放广告:" + name);
        // 更改标志位
        this.flag = !this.flag;
        // 广告放过了,该放节目了
        this.notifyAll();
    }
}

(2)AD.java

package com.pbx.lesson04;

/**
 * @author BruceXu
 * @date 2020/9/18
 */
public class AD extends Thread {
    private final TV tv;

    public AD(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.playAD("第" + i + "个广告,今年过节不收礼,收礼只收脑白金");
        }
    }
}

(3)Show.java

package com.pbx.lesson04;

/**
 * @author BruceXu
 * @date 2020/9/18
 */
public class Show extends Thread {

    private final TV tv;

    public Show(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.playShow("第" + i + "个节目,NBA全明星赛");
        }
    }
}

(4)Main.java

package com.pbx.lesson04;

/**
 * @author BruceXu
 * @date 2020/9/18
 */
public class Main {
    public static void main(String[] args) {
        TV tv = new TV();
        new Show(tv).start();
        new AD(tv).start();
    }
}

技术分享图片

6. 线程池法 (了解为主)

原文:https://www.cnblogs.com/primabrucexu/p/13694077.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!