认识wait(),notify(),notifyAll()方法

        线程最大的问题,就是在CPU当中是抢占式执行,随机调度的。但是,为了控制线程执行的先后顺序,可以通过一些api来让线程主动放弃cpu的使用权。从而达到让有些线程先执行,有一些后执行的效果。


目录

一、wait()方法的使用

wait()方法的执行逻辑:

代码实现:

 wait()方法和sleep()方法的区别?

二、notify()方法

      含义:

      代码实现:

     注意事项:

三、notifyAll()方法 

       来一段代码,观察一下notify()方法:

       再观察一下notifyAll()方法:

四、A,B,C三个线程,如何按照顺序输出A,B,C


一、wait()方法的使用

        wait()方法是Object类的成员方法,它的含义是,如果在某一个线程的任务当中调用了object.wait()方法,会让当前的线程进入阻塞状态.

        如果wait()方法的参数列表为空,那么就会"死等",直到有其他的线程来唤醒当前线程。在等待的过程中,"锁"会释放掉。允许其他线程继续获取锁(object)。

        但是在Object类当中,wait()方法是存在多态的。如果不添加任何参数,那么默认是死等。直到有其他线程把它唤醒。如果添加了参数,那么含义就是:wait()到一定的时间。等到了时间,就会继续尝试获取锁。


但是线程一运行,就会出现以下报错:

IllegalMonitorStateException

把上面这个单词拆分一下,就变成: Illegal  MonitorState Exception

即:非法的锁监视器异常;那么这个异常,是哪里存在状态异常呢?

锁的状态,无非就是2种,一种是被加锁的状态,另外一种就是被解锁的状态。

此时,锁状态异常就是,当程序期待锁的状态是什么,但是锁的状态并不是这个状态,这也就触发了锁状态异常。


wait()方法的执行逻辑:

wait()是Object类的方法,调用这个方法需要对象。如果调用wait()方法的对象已经被线程加锁了,再调用wait()方法,就会让加锁的线程释放锁,其他线程可以获取到当前对象的对象锁,然后自己进入阻塞状态。但是,如果调用的对象没有被线程加锁,直接调用wait(),就会抛出锁状态异常:


代码实现:

public class ThreadDemo16 {
    public static void main(String[] args) {
        Object object=new Object();

        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object){
                    System.out.println("阻塞之前");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("阻塞之后");
                }
            }
        });
        thread.start();
    }
}

可以看到,针对object对象加锁之后,只能看到阻塞之前的情况,无法看到阻塞之后这一句话。

但是进程一直没有结束。

可以看到,运行结果一直是:阻塞之前...... 


 wait()方法和sleep()方法的区别?

         ①语法上面的不同,wait()方法属于Object类的成员方法,sleep()方法属于Thread类的静态方法;

         ②二者都会让线程进入阻塞状态,但是区别在于:

          当一个线程调用了Thread.sleep(t)方法之后,会让自身进入阻塞状态,达到了时间t(单位为毫秒)之后,线程会继续回到就绪队列当中,等待操作系统的调度。如果Thread.sleep()出现在一个被加锁的代码块或者方法当中,不会释放锁

         但是,如果在synchronized代码块当中,如果被加锁的对象调用了wait()方法之后,会释放锁,让其他线程可以继续获取当前对象的锁,然后当前线程进入阻塞状态。

        如果wait()方法没有指定时间,就会默认死等下去,直到其他线程唤醒当前线程。如果指定了wait()的时间,那么到了指定的时间之后,当前线程会继续尝试针对对象加锁,执行接下来的任务。


二、notify()方法

      含义:

       notify()方法是object类的成员方法。当调用这个方法之前,需要线程针对某一个对象object来加锁,在加锁之后,在接下来的同步代码块(synchronized修饰)当中才可以调用notify()方法,此方法用来唤醒进入阻塞状态的线程。

       但是"唤醒"之后,调用notify()方法的线程不会立即释放锁,仍然会继续执行任务,直到执行完同步代码块的任务之后,才最把锁释放。

       唤醒之后,原来调用object.wait()的线程会从阻塞队列当中离开,尝试获取锁;但是,因为此时无法再次获取锁,所以,又会进入阻塞队列。


     

  


      代码实现:

 运行结果:


注意事项:

       ①两个线程操作的wait(),notify()操作,一定要操作的是同一个对象,才会让wait()方法和notify()方法都生效。

       ②由于操作系统针对线程的调度是随机的,因此,也有可能让后面start()的t2线程先执行,那么,如果t2先notify()的话,t2就相当于空打一炮,t1依然会在执行完wait()方法之后一直阻塞。


三、notifyAll()方法 

       当多个线程尝试获取同一个对象(object)的锁的时候,只会有一个线程获取到锁,其他线程进入阻塞等待状态。那么此时,如果获取锁的线程调用了object.notifyAll()方法之后,会唤醒其他都正在等待的线程。

      与notify()不同的是,notify()只会随机唤醒一个正在阻塞等待的线程。而notifyAll()是全部唤醒。


       来一段代码,观察一下notify()方法:

      给出一个业务场景,同时启动7个线程,每个线程的任务当中,都会针对同一个对象(object)加锁,这会造成其中一个线程获取到锁,其他线程阻塞等待的情况。

     所有线程都启动之后,主线程调用object.notify()方法,观察一下运行的情况:

 public static void main(String[] args) {
        Object lock=new Object();
        for (int i=1;i<=7;i++)
        {
            int I = i;
            //让每个线程都针对lock对象加锁,其中一部分线程去阻塞等待
            Thread t1=new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程"+ I+"正在等待");
                    synchronized (lock){
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程"+I+"已经被唤醒");
                }
            });
            t1.start();
        }
        //让其他线程都去先执行任务
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //主线程尝试唤醒其他线程
        synchronized (lock){
            lock.notify();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

       运行结果截图:

      

     可以看到,此时notify()方法仅仅是随机唤醒了一个正在等待的线程,但是进程并没有结束。其他没有被唤醒的线程继续阻塞等待


           再观察一下notifyAll()方法:

  public static void main(String[] args) {
        Object lock=new Object();
        for (int i=1;i<=7;i++)
        {
            int I = i;
            //让每个线程都针对lock对象加锁,其中一部分线程去阻塞等待
            Thread t1=new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程"+ I+"正在等待");
                    synchronized (lock){
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程"+I+"已经被唤醒");
                }
            });
            t1.start();
        }
        //让其他线程都去先执行任务
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //主线程尝试唤醒其他线程
        synchronized (lock){
            lock.notifyAll();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

            运行结果截图:

           

可以看到,此时唤醒了所有正在等待的线程,进程顺利结束。


四、A,B,C三个线程,如何按照顺序输出A,B,C?

       思路:这一题可以使用join来完成,也可以使用notify(),wait()这两个方法配合完成。

       如果使用wait(),notify()来配合完成,首先需要两个对象,一个是object1,另外一个是object2.用来完成线程之间的相互通知。

      就需要考虑使用,先让B,C线程并发执行,因为C在B之后输出,因此在线程B输出完"B",之后,会通知线程C输出B。

       接着,让线程A最后启动。线程A启动之后,通知线程B执行。

  



图解:


代码实现:

public class ThreadHomeWork3 {
    public static void main(String[] args) {
        Object lock1=new Object();
        Object lock2=new Object();
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
                //thread1线程优先唤醒lock1
                synchronized (lock1){
                    lock1.notifyAll();
                }
            }
        },"A");
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock1){
                    try {
                        lock1.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(Thread.currentThread().getName());
                synchronized (lock2){
                    lock2.notifyAll();
                }
            }
        },"B");
        Thread t3=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock2){
                    try {
                        lock2.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName());
            }
        },"C");
        t2.start();
        t3.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.start();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值