Java多线程 线程通信问题

线程的通信

线程通信的例子:
在这里插入图片描述
涉及到的线程通信的三个方法:

  • ①wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
  • ②notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,会唤醒优先级高的一个
  • ③notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
    说明:
  • 1,wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
  • 2,wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出现异常
  • 3,wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中的(因为,任何一个类的对象都可以充当同步监视器,我们要用作为同步监视器的对象调用这三个方法,要保证任何一个对象都要有此方法,所以要在Object中定义
package com.atguigu.java2;
/**
 * 线程通信
 */
class Number implements Runnable{
    private int number=1;          //相当于共享数据
    @Override
    public void run() {
        while (true){
            synchronized (this) {
                notify();    //唤醒一个,notifyAll唤醒全部。完整为this.notify()
                if (number<=100){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+number);
                    number++;
                   //number增加之后,调用wait()方法,使得调用wait()方法的线程阻塞一下
                    try {
                        wait();  //完整为this.wait()
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    break;
                }
            }
        }
    }
}
public class CommunicationTest {
    public static void main(String[] args) {
        Number number=new Number();
        Thread t1=new Thread(number);
        Thread t2=new Thread(number);
        t1.setName("线程一");
        t2.setName("线程二");
        t1.start();
        t2.start();
    }
}

常问面试题:

sleep和wait的异同:

相同点:
一旦执行方法,都可以使当前的线程进入阻塞状态。
不同点:
①两个方法声明的位置不同:Thread类中声明静态的sleep()方法;Object类中声明wait();
②调用的范围不同:sleep()方法可以在任何需要的场景下调用;而wait()方法必须由同步监视器调用(即wait()方法必须使用在同步代码块或同步方法中);
③关于是否释放同步监视器的问题:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁(同步监视器),而wait()会释放锁。

线程通信经典问题:生产者/消费者问题

在这里插入图片描述
分析:
①是否为多线程问题?
是,生产者线程、消费者线程。

②是否有共享数据?
有,店员或产品数量。

③如何处理线程安全问题?
三种同步机制。
④是否涉及到线程通信
是。

package com.atguigu.java2;
/**
 * 线程通信的应用
 */
class Clerk {
    private int productCount = 0; //产品数量开始时为0

    //增加产品数量(+)
    public synchronized void produceProduct() {  //同步方法,同步监视器为clerk。否则增加产品数后若阻塞可能会出现线程安全问题
        if (productCount < 20){
            productCount++;   //先增加一个产品。否则会出现 “开始生产第0个产品”
            System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品");
            notify();  //只要生产了一个产品便可以唤醒消费者消费
        } else {  //达到20,暂停生产
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //减少产品数量(-)
    public synchronized void consumeProduct() {
        if (productCount>0){  //产品数大于0才能减少
            System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
            productCount--;
            notify();  //只要消费了一个产品便可以唤醒生产者生产
        }else{  //产品数量<=0。暂停消费
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

class Producer extends Thread{    //注意生产者是线程(此处用继承方式)
    //生产者、消费者 *共用* Clerk,因此通过以下方式
    private Clerk clerk;
    public Producer(Clerk clerk) {  //构造器
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName()+":开始生产产品。。。");    //继承方式可以省略Thread.currentThread.
        while (true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}
class Consumer extends Thread {     //消费者
    //消费者处理方式和生产者一样
    private Clerk clerk;

    public Consumer(Clerk clerk) {  //构造器
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName() + ":开始消费产品。。。");    //继承方式可以省略Thread.currentThread.
        while (true) {
            try {
                Thread.sleep(20);  //生产快,消费慢但有两个消费者
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}

public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk=new Clerk();    //clerk自始至终造了一个
        Producer p1 = new Producer(clerk);
        p1.setName("生产者1");

        Consumer c1=new Consumer(clerk);
        c1.setName("消费者1");
        Consumer c2=new Consumer(clerk);
        c2.setName("消费者2");
        p1.start();
        c1.start();
        c2.start();
    }
}


同步方法
生产者握住同步监视器clerk之后进入同步方法produceProduct(),这时消费者就不能进入同步方法consumeProduct(),因为二者同步监视器均为this,即clerk。
只有生产出商品后,才有可能抢到同步监视器去消费。这样就安全了。(注意根据题意配合wait()、notify())

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

过期动态

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

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

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

打赏作者

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

抵扣说明:

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

余额充值