Java中多线程间的等待唤醒机制


前言

我们根据之前所学知识,能够使用java中多线程机制模拟出电影院买票流程,但是通过下面的图我们可以看出,三个线程是属于同一类型的线程,它们都是在执行买票这个操作。那么java中又是如何处理不同种类线程间的通信问题的呢?那么就涉及到java多线程间的等待唤醒机制。
在这里插入图片描述

知识点:等待唤醒机制

1. 需求:实现生产线与消费线之间的通信,实现效果是生产者生产一个,消费者消费一个。

  • 生产者角度:有资源尚未被消费,就等待不生产并且通知消费者消费
  • 消费者角度,如果有资源就消费,如果没有资源就等待并通知生产者生产

2. Oject类中

  • void notify ()
    唤醒在此对象监视器上等待的单个线程。
  • void notifyAll ()
    唤醒在此对象监视器上等待的所有线程。
  • void wait ()
    在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。
  • void wait (long timeout)
    在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。

3. 代码示例

  • 条件:
    1.生产者线程
    2.消费者线程
    3.流通资源
public class 线程间等待唤醒机制 {
    
    
    public static void main(String[] args) {
    
    
        //创建共享资源
        Student student = new Student();
        //将资源传递给线程,所以线程之间是共享资源的
        SetThread  th1= new SetThread(student);
        GetThread th2 = new GetThread(student);
        th1.start();
        th2.start();

    }

}
//1.生产线程
class SetThread extends Thread{
    
    
    private Student student;
    int i=0;
    public SetThread(Student student) {
    
    
        this.student = student;
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            synchronized (student){
    
    
                //能够进入该代码,说明有资源,生产线程就应该等待
                if(student.flag){
    
    
                    try {
    
    
                        student.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                if (i % 2 == 0) {
    
    
                    student.name = "张三";
                    student.age = 19;
                } else {
    
    
                    student.name = "李四";
                    student.age = 23;
                }
                //更改标记
                student.flag=true;
                //通知消费线程去消费
                student.notify();
            }

            i++;
        }



    }
}
//2.消费线程
class GetThread extends Thread{
    
    
   private  Student student;
    public GetThread(Student student) {
    
    
        this.student=student;
    }

    @Override
    public void run() {
    
    
        while (true){
    
    
            //既然对象是一个,我们可以将其作为锁对象。这样保持两个同步代码块的锁一致
            synchronized (student){
    
    
                //如果没有资源,进入程序等待
                if(!student.flag){
    
    
                    try {
    
    
                        student.wait();//线程一旦调用等待wait()方法,就会释放锁,即线程没出同步代码块提前释放锁。下次被唤醒 ,从此处唤醒
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }

                }
                System.out.println(student.name + "," + student.age);
                //消费完资源后,更改标记,通知生产者生产
                student.flag=false;
                student.notify();
            }
        }

    }
}
//资源
class Student{
    
    
    public String name;
    public int age;
    //定义一个标记
    public boolean flag=false;//false表示没有资源,true表示有资源
}

执行效果图:
在这里插入图片描述

4.sleep()方法和wait()方法的区别

  • 共同:都可以让线程处于一种阻塞状态
  • wait()方法:导致当前线程处于阻塞状态,直到被另一个线程调用notify ()或notifyAll ()来唤醒,调用该方法,必须要获得锁对象,所以该方法只能在同步方法和同步代码块中用。(notify ()也是需要在同步方法或同步代码块中调用)。wait()方法一旦执行会释放锁,可以使用notify ()直接唤醒。
  • sleep()方法一旦休眠不会释放锁(必须设置时间量),时间不到只能调用Interreput()强行打断。

作业

  • 运用所学知识,编写代码。
    需求:写两个线程,一个线程打印1-52,一个线程打印A-Z,打印结果是12A34B56C78D-----5152Z(即两个数字一个字母)。

  • 代码答案

public class day23作业1 {
    
    
    public static void main(String[] args) {
    
    
        //创建共享资源
        PrintFunc printFunc = new PrintFunc();
        //将共享资源传入两个子线程中
        DigitalThread th1 = new DigitalThread(printFunc);
        LetterThread th2 = new LetterThread(printFunc);
        th1.start();
        th2.start();

    }
}
//1.数字线程
class DigitalThread extends Thread{
    
    
    int i=1;
    private PrintFunc printFunc;

    public DigitalThread(PrintFunc printFunc) {
    
    

        this.printFunc = printFunc;
    }

    @Override
    public void run() {
    
    
        while (i<=52){
    
    
            synchronized (printFunc){
    
    
                //进入这段代码,代表打印了数字
                if(printFunc.flag){
    
    
                    try {
    
    
                        printFunc.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                System.out.print(i);
                i++;
                System.out.print(i);
                //打印了数字,更改标志
                printFunc.flag=true;
                //通知字母线程打印
                printFunc.notify();
            }
            i++;
        }
    }
}
//2.字母线程
class LetterThread extends Thread{
    
    
    char ch='A';
    private PrintFunc printFunc;

    public LetterThread(PrintFunc printFunc) {
    
    
        this.printFunc = printFunc;
    }

    @Override
    public void run() {
    
    
        while (ch<=90){
    
    
            synchronized (printFunc){
    
    
                //进入该代码,表示尚未打印数字
                if(!printFunc.flag){
    
    
                    try {
    
    
                        printFunc.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                //打印了数字,开始打印字母
                System.out.print(ch);
                //更改标志
                printFunc.flag=false;
                printFunc.notify();
            }
            ch++;

        }
    }
}
//3.共享资源
class PrintFunc{
    
    
    //定义一个标签
    boolean flag=false;//false代表尚未打印数字
}
  • 结果
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_46988935/article/details/112916318