Java多线程~线程间的通信、线程的等待与唤醒、线程通信的模拟使用(面包店买卖面包案例)

目录

线程间的通信

线程通信的概念

线程通信的使用方式

 wait()-让线程进行等待

notify()-唤醒等待的线程

 notifyAll()-唤醒等待中的线程

线程通信的应用案例

完整代码

运行结果 


线程间的通信

多线程的优势是提高cpu的利用率,但使用时需要注意:当执行时间比较长的任务时,可能存在线程安全的问题,在以上的前提下,需要保证线程的执行有一定的顺序性,因此就有了线程通信的概念.

线程通信的概念

线程通信,就是一个以线程通知的方式,唤醒某些等待的线程(也可以在某些条件下,让线程进行等待),这样就可以让线程间通过通信的方式满足一定的顺序性.

线程通信的使用方式

        synchronized(某个对象) { //对"某个对象"申请加锁,多个线程对同一个对象加锁是同步互斥的
            while(如果不满足执行条件) {
                某个对象.wait();//释放某个对象的锁,让当前线程进行等待
            }
            //满足执行条件
            执行业务;
            某个对象.notify() / 某个对象.notifyAll();//在当前线程释放锁以后以通知的方式唤醒之前调用wait等待的线程
        }

 对于上述使用方式的解析

· synchronized(某个对象):对某个对象申请加锁,多个线程对同一个对象加锁就是同步互斥的.

· 同步互斥:一个时间点最多只有一个线程申请锁成功,进入synchronized内,执行同步代码.

· 某个对象.wait():释放某个对象的锁,让当前线程进行等待

· 某个对象.notify():在当前线程释放锁以后以通知的方式,唤醒之前调用wait等待的线程.

①notify()是随机唤醒一个,②notifyAll()是唤醒全部

 

 wait()-让线程进行等待

某个线程调用wait方法后,wait做的事情

①使当前执行代码的线程进行等待.(把线程放在等待队列中)

②释放当前的锁

③满足一定条件时被唤醒,重新尝试获取这个锁

wait需要搭配synchronized来使用,否则使用wait会直接抛出异常

wait结束等待的条件: 

①其他线程调用该对象的notify()方法

②wait等待时间超时(wait(1000)类似这种使用)

③其他线程调用该等待线程的interrupted方法,导致wait抛出异常

    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        synchronized (lock) {
            System.out.println("等待中");
            lock.wait();
            System.out.println("等待结束");
        }
    }

  

notify()-唤醒等待的线程

notify()方法是唤醒等待中的线程

· notify方法也要在同步方法或同步代码块中调用,该方法是用来通知那些可能等待该对象的对象锁的其他线程,对其发出通知notify,并使它们重新获取该对象的对象锁.

· 如果有多个线程目前都处于等待状态,则由线程调度器随机挑出一个呈wait状态的线程(无"先来后到原则").

· 在notify方法调用后,当前线程不会立刻释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁

notify方法使用示例

public class UseDemo {
    private static final Object  lock = new Object();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synchronized(lock) {
                        System.out.println("wait 等待中");
                        lock.wait();
                        System.out.println("wait 等待结束");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    System.out.println("notify 开始通知");
                    lock.notify();
                    System.out.println("notify 通知结束");
                }
            }
        }).start();
    }
}

 

 notifyAll()-唤醒等待中的线程

notify方法是随机唤醒一个等待中的线程,而notifyAll方法是一次性唤醒所有等待中的线程,这些被唤醒的线程再进行竞争锁.

使用示例:

public class UseDemo {
    private static final Object  lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synchronized(lock) {
                        System.out.println("wait 等待中");
                        lock.wait();
                        System.out.println("wait 等待结束");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        for (int i = 0; i < 6; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        System.out.println(Thread.currentThread().getName() + "notify 开始通知");
                        lock.notifyAll();
                        System.out.println(Thread.currentThread().getName() + "notify 通知结束");
                    }
                }
            },"线程"+(i+1)).start();
        }
    }
}

 

通过运行结果可以看出,notifyAll()唤醒所有等待中的线程后,这些被唤醒的线程再进行竞争锁,而不是按照线程定义顺序来执行. 

线程通信的应用案例

下述代码演示wait()与notifyAll方法的综合使用

案例描述:

· 有3个面包师傅,每个师傅每次生产5个,需要一直生产,当库存大于100时不再生产

· 有10个消费者,每个消费者每次购买2个,需要一直消费,当库存小于0时不再售卖

完整代码

package threadcommunity;

/**
 * Created with IntelliJ IDEA.
 * Description: 线程通信的使用案例,模拟面包店买卖面包
 * User: Li_yizYa
 * Date: 2022—05—15
 * Time: 23:53
 */

/**
 * 业务描述:
 * 3个面包师傅,每个师傅每次生产5个,需要一直生产
 * 10个消费者,每个消费者每次消费2个,需要一直消费
 */
public class Bakery {
    //共享变量,当前面包的库存数,默认初始化为0
    private static int STORE;

    private static final Object lock = new Object();

    public static void main(String[] args) {
        //模拟面包师傅生产面包
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //一直生产
                    while (true){
                        try {
                            //准备生产
                            synchronized (lock){
                                //不满足生产条件
                                while (STORE + 5 > 100){
                                    lock.wait();
                                }
                                //满足生产条件
                                STORE += 5;//生产
                                System.out.printf("%s 生产了5个面包,当前库存:%s\n",
                                        Thread.currentThread().getName(),
                                        STORE);
                                lock.notifyAll();
                                Thread.sleep(1000);//间隔一段时间打印
                            }
                            Thread.sleep(100);//涉及jvm对synchronized优化,暂时不管
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "面包师傅["+(i+1)+"]").start();
        }
        //模拟消费者购买面包
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //一直消费
                    while(true) {
                        try {
                            //准备消费
                            synchronized(lock) {
                                //不满足消费条件
                                while(STORE-2 < 0) {
                                    lock.wait();
                                }
                                //满足消费条件
                                STORE -= 2;
                                System.out.printf("%s 购买了两个面包,当前库存: %d\n",
                                        Thread.currentThread().getName(),
                                        STORE);
                                lock.notifyAll();
                                Thread.sleep(1000);
                            }
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            },"消费者["+(i+1)+"]").start();
        }
    }
}

运行结果 

程序在一直循环执行,并且通过线程通信使得运行结果一直满足案例的要求

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_yizYa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值