Java多线程:线程间通信(等待/通知机制)

培训教学 潘老师 5个月前 (11-09) 119 ℃ (0) 扫码查看

本文主要重点讲解Java多线程:线程间通信(等待/通知机制)问题。

等待/通知机制概念

等待/通知机制在我们生活中比比皆是,一个形象的例子就是厨师和服务员之间就存在等待/通知机制。

  1. 厨师做完一道菜的时间是不确定的,所以菜到服务员手中的时间是不确定的;
  2. 服务员就需要去“等待(wait)”;
  3. 厨师把菜做完之后,按一下铃,这里的按铃就是“通知(nofity)”;
  4. 服务员听到铃声之后就知道菜做好了,他可以去端菜了。

在Java多线程中理解就是:

等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()/notifyAll()方法,线程A收到通知后退出等待队列,进入可运行状态,进而执行后续操作。上面这样的一个过程就是等待/通知机制。

等待/通知机制实现

Object类中的wait()方法,可以使执行当前代码的线程等待,暂停执行。直到接到通知或被中断为止。

注意:wait()方法只能在同步代码块中由锁对象调用,且调用wait()方法后,当前线程会释放锁。

Object类的notify()方法可以唤醒处于等待的线程,该方法也必须在同步代码块中由锁对象调用。如果有多个等待的线程,notify()只能唤醒其中一个,具体唤醒哪一个是不知道的。被notify()的线程需要重新去竞争锁才能被执行。

没有使用锁对象就调用wait()/notify()方法会产生异常:IllegalMonitorStateException。

wait()代码实例:

public class Test01 {

    public static void main(String[] args) throws InterruptedException {
        String test = "abc";
        String another = "def";
        System.out.println("同步代码块前的代码");
        synchronized (test) {
            try {
                System.out.println("wait前的代码");
                // another.wait();  只有被锁住的对象才能调用wait()方法
                test.wait();
                System.out.println("wait后的代码");
            } catch (IllegalMonitorStateException e) {
                e.printStackTrace();
            }
        }
        System.out.println("同步代码块后的代码");
    }
}

wait()/notify()代码示例:

/**
 * 需要通过notify唤醒线程
 */
public class Test02 {

    public static void main(String[] args) {
        String str = "wa";

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (str) {
                    System.out.println("线程1开始等待");
                    try {
                        str.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程1被唤醒并执行结束了");
                }
            }
        }, "Thread1");

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (str) {
                    System.out.println("线程2唤醒线程1");
                    str.notify();
                }
            }
        }, "Thread2");

        thread1.start();
        thread2.start();
    }
}

执行了notify()的线程并不会立即释放锁,而是执行完同步代码块的所有代码后才会释放锁

interrupt()方法会中断wait()

当线程调用wait()处于等待状态时,调用线程对象的interrupt()方法会中断线程的等待状态,产生InterruptedException异常。

/**
 * interrupt()会中断线程的wait状态
 */
public class Test04 {
    public static void main(String[] args) throws InterruptedException {
        SubThread subThread = new SubThread();
        subThread.start();
        TimeUnit.SECONDS.sleep(1);
        subThread.interrupt();
    }

    private static final Object lock = new Object();

    static class SubThread extends Thread {
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println("subThread wait");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    System.out.println("wait等待被中断了");
                }
                System.out.println("subThread end wait");
            }
        }
    }
}

wait(long)方法的使用

如果在指定时间内没有被唤醒,那么线程会自动唤醒。

public class Test06 {
    public static void main(String[] args) {
        SubThread subThread = new SubThread();
        subThread.start();
    }

    static final Object lock = new Object();

    static class SubThread extends Thread{
        @Override
        public void run() {
            synchronized (lock) {
                try {
                    System.out.println("开始等待");
                    lock.wait(5000);
                    System.out.println("等待结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

notify()和notifyAll()的区别

1) 唤醒数量不同

notify()方法只会随机唤醒等待队列中的一个线程,而notifyAll()方法则会唤醒等待队列中的所有线程。

2)调用方式不同

notify()和notifyAll()方法都必须在同步代码块中调用,并且必须包含在synchronized块中,且必须是该对象的监视器对象才能够调用。而且只有在获取了锁之后才能调用,否则会抛出IllegalMonitorStateException异常。

3) 竞争情况不同

notify()方法只会唤醒等待队列中的一个线程,并使其与其他线程竞争获取锁,这可能会导致某些线程无法被唤醒或者一直处于等待状态。

notifyAll()方法则会唤醒等待队列中的所有线程,并使它们竞争获取锁,这样可以使所有线程都有机会获取锁并进入运行状态,从而避免了一些线程一直处于等待状态。

总结

以上就是Java多线程:线程间通信(等待/通知机制)的基础内容,如下想要更深层次地理解该机制,可以参考下一篇文章:Java多线程:线程间通信(生产者消费者模式)


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/teach/10920.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】