java多线程 (三)线程通信

175 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。

什么是线程通信

需要多个线程配合完成一件事情, 如何让多个线程能够合理的切换就是线程通信需要考虑的问题. 重点在于配合。

生产者消费者模式

生产者和消费者模式为最经典的线程通信案例

• 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费

• 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止

• 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止

生产商品实体类

​
​
/**
 * @Classname Product
 * @Description
 * @Date 2022/4/5 11:50
 * @Author Lee
 */
​
//生产商品的实体类
public class Product {
​
    private   String  name; //馒头   玉米饼
    private   String  color;//白色    黄色
​
    private   boolean  flag;//库存的信号
​
    public Product(){
​
    }
    public Product(String name, String color) {
        this.name = name;
        this.color = color;
    }
​
    public boolean isFlag() {
        return flag;
    }
​
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public String getColor() {
        return color;
    }
​
    public void setColor(String color) {
        this.color = color;
    }
​
    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + ''' +
                ", color='" + color + ''' +
                '}';
    }
}
​
​

synchronized实现

​
​
/**
 * @Classname TestA
 * @Description
 * @Date 2022/4/5 11:50
 * @Author Lee
 */
public class TestA {
    public static void main(String[] args) {
        Product product = new Product();
        new Thread(new ProduceRunnable(product)).start();
        new Thread(new ConsumeRunnable(product)).start();
    }
}
//生产者
class ProduceRunnable implements Runnable{
    private static int num = 0;
    private Product product;
​
    public ProduceRunnable(Product product) {
        this.product = product;
    }
​
    @Override
    public void run() {
        while (true) {
            synchronized (product) {
                //进行判断  如果信号是一个true代表库存中有内容 不需要在生产
                if(product.isFlag()){
                    //当前线程处于等待状态
                    try {
                        product.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序如果可以运行到这个位置 代表没有库存 继续生产
                if (num % 2 == 0) {
                    product.setColor("白色");
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    product.setName("馒头");
                } else {
                    product.setColor("黄色");
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    product.setName("玉米饼");
                }
​
                System.out.println("生产者生产了:" + product.getColor() + "-" + product.getName());
                product.setFlag(true);//代表已经生产了商品
                product.notify();//通知消费者进行消费
                num++;
            }
​
​
        }
    }
}
//消费者
class ConsumeRunnable implements Runnable{
​
    private Product product;
​
    public ConsumeRunnable(Product product) {
        this.product = product;
    }
​
    @Override
    public void run() {
        while (true){
            synchronized (product) {
               //如果进入if 代表当前没有库存的  消费者进行等待
                if(!product.isFlag()){
                    try {
                        product.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("消费者消费了:" + product.getColor() + "-" + product.getName());
                product.setFlag(false);//消费结束把flag变成false
                product.notify();//通知生产者进行生产商品
            }
        }
    }
}
=
​

这里的wait() 和 notify() 是Object总类中的方法 必须在有锁代码实现

wait()一直等待 wait(2000) 指定等待时间 wait(200,200)指定等待时间 毫秒+纳秒 将线程从运行 态变为阻塞态

notify() 随机唤醒一个等待的线程 如果在多个生产者和消费者的情况下 可能会出现唤醒线程错误

Lock实现

​
​
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
​
/**
 * @Classname TestB
 * @Description
 * @Date 2022/4/5 12:00
 * @Author Lee
 */
public class TestB {
    public static void main(String[] args) {
        Product product = new Product();
        Lock lock = new ReentrantLock();
        //生产者等待队列
        Condition Producecondition = lock.newCondition();
        //消费者等待队列
        Condition Consumecondition = lock.newCondition();
        new Thread(new ProduceRunnable2(product,lock,Producecondition,Consumecondition)).start();
        new Thread(new ConsumeRunnable2(product,lock,Producecondition,Consumecondition)).start();
    }
}
//生产者
class ProduceRunnable2 implements Runnable{
    private static int num = 0;
    private Product product;
    Lock lock;
    Condition Producecondition;
    Condition Consumecondition;
​
    public ProduceRunnable2(Product product, Lock lock, Condition producecondition, Condition consumecondition) {
        this.product = product;
        this.lock = lock;
        Producecondition = producecondition;
        Consumecondition = consumecondition;
    }
​
    @Override
    public void run() {
        while (true) {
           lock.lock();
                //进行判断  如果信号是一个true代表库存中有内容 不需要在生产
                if(product.isFlag()){
                    //当前线程处于等待状态
                    try {
                       Producecondition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序如果可以运行到这个位置 代表没有库存 继续生产
                if (num % 2 == 0) {
                    product.setColor("白色");
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    product.setName("馒头");
                } else {
                    product.setColor("黄色");
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    product.setName("玉米饼");
                }
​
                System.out.println("生产者生产了:" + product.getColor() + "-" + product.getName());
                product.setFlag(true);//代表已经生产了商品
                Consumecondition.signal();//通知消费者进行消费
                num++;
            lock.unlock();
​
​
        }
    }
}
//消费者
class ConsumeRunnable2 implements Runnable{
    Lock lock;
    private Product product;
    Condition Producecondition;
    Condition Consumecondition;
​
    public ConsumeRunnable2(Product product,Lock lock, Condition producecondition, Condition consumecondition) {
        this.lock = lock;
        this.product = product;
        Producecondition = producecondition;
        Consumecondition = consumecondition;
    }
​
    @Override
    public void run() {
        while (true){
            lock.lock();
                //如果进入if 代表当前没有库存的  消费者进行等待
                if(!product.isFlag()){
                    try {
                        Consumecondition.await(); //把线程加入消费者队列等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("消费者消费了:" + product.getColor() + "-" + product.getName());
                product.setFlag(false);//消费结束把flag变成false
                Producecondition.signal();//通知生产者进行生产商品
            lock.unlock();
        }
    }
}
​

Condition用来替代传统的Object的wait()、notify()实现线程间的协作 它可以精确的唤醒等待的线程 与之相对应的方法是await(), signal()

一个Condition包含一个等待队列。一个Lock可以产生多个Condition,所以可以有多个等待队列