您的位置 首页 java

Java 17 线程的通信 wait/notify 机制

Java 17 线程的通信 wait/notify 机制

前面说了很多 线程 的基础知识点,以及同步和异步, synchronized volatile 关键字的使用。这一节内容就来说说线程间如何进行通信。

线程之间有了通信, 就不再是单独的个体,可以进行线程间的协作了。

我们来看个例子,在不引入线程通信的情况下如何多个线程间进行沟通。

例子很简单,先来一个操作类, 可以保存数据, 并查看当前保存条数。一个线程保存数据, 一个线程读取数据。这里例子没有太大的意义,就是为了演示该特点。

没有线程通信机制的通信。

代码如下:

 class Send {
     private  volatile List<String> sendList = new ArrayList<>();

    public  void  add() {
        sendList.add("18500000001");
    }

    public int size() {
        return sendList.size();
    }
}  

两个线程, 一个保存数据,

 class ThreadA  extends  Thread {
    private Send send;

    public  Thread A(Send send) {
        this.send = send;
    }

    @Override
    public void run() {
        try {
            for (int i = 1; i <= 10; i++) {
                send.add();
                System.out.println("添加了 " + i + " 个元素");
                TimeUnit.SECONDS.sleep(1);
            }
        } catch ( Exception  e) {
            System.out.println(e.getMessage());
        }
    }
}  

另外一个读取数据, 读取到一定的数据, 就开始做一些操作。这里为了方便, 直接 new 个异常,结束掉线程循环。

 class ThreadB extends Thread {
    private Send send;

    public ThreadB(Send send) {
        this.send = send;
    }

    @Override
    public void run() {
        try {
            while (true) {
                if (send.size() == 8) {
                    System.out.println("send 的发送数据已经够 8 个了。准备发送数据。");
                    throw new  interrupt edException("为了退出 while 循环,抛个异常玩玩。");
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}  

完整代码如下:

运行效果:

但是这样是有问题的, 因为线程的无序随机性, 如果判断里面执行的时间过长,已经超过了需要判断的条件, 可能就会出现线程内死循环,就无法退出循环了。

线程的通信 wait/notify 机制

wait 和 notify 分别是什么呢? 根据英语直译。等待和通知。我们一起学 java 好吗?学 Java 好吗? Java 好吗?好吗?这个就叫做等待, 等待着你的应答。然后你说好的, 相当于给我了通知。告诉我,可以。

并且在 java.lang .Object 中, 从基类中就有对应的方法。 方法如下:

当然有很多的例子能够来说明等待和通知的机制。去饭店点餐,你点了之后,就要等待着厨师做饭,服务员端菜,的过程。你等待的过程就是 wait 的过程。做好餐了服务员说你的餐来喽。就是 notify 通知你可以吃饭了。

通俗的来说: wait 使线程暂停运行,而 notify 通知暂停的线程继续运行。

大概的运行图如下:

写代码之前看一下这两个方法的定义:

 public final void wait() throws InterruptedException
public final native void notify()  

对于 wait 方法需要注意的是,该方法必须在同步方法或者同步块中调用,否则会出现异常 java.lang.IllegalMonitor State Exception

简单的演示代码:

wait

代码如下:

 public class WaitNotify02 {
    public  static  void main(String[] args) throws InterruptedException{
        WaitNotify02 waitNotify02 = new WaitNotify02();
        waitNotify02.wait();
    }
}  

运行效果:

  javac  -encoding UTF-8 WaitNotify02.java && java WaitNotify02
Exception in thread "main" java.lang.IllegalMonitorStateException: current thread is not owner
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Object.wait(Object.java:338)
at WaitNotify02.main(WaitNotify02.java:4)  

加上同步块, 调整代码运行查看效果:

这种情况下,线程就一直在等待。一直等着 notify 的通知。当然根据上面的方法, 也有另外两个重载方法,一个是传入毫秒, 一个是传入毫秒的同时, 传入 纳秒 。这个就是不会傻傻的等, 有个超时时间。

测试代码如下:

既然已经在等待了, 就不要让 wait 等着急了。notify 快来啊!

这里写一个 wait 和 notify 的小例子。

notify

测试的 main 方法类中, 显示一个 字符串 。 然后在 notify 方法中把字符串的修改了。然后看等待方法中的响应。

创建两个线程,以及一个测试类。

线程一,主要是 wait 演示

 class ThreadA extends Thread {
    private WaitNotify05 waitNotify05;

    public ThreadA(WaitNotify05 waitNotify05) {
        this.waitNotify05 = waitNotify05;
    }

    @Override
    public void run() {
        try {
            synchronized (waitNotify05) {
                System.out.println(Thread.currentThread().getName() + " s= " + waitNotify05.s);
                System.out.println(Thread.currentThread().getName() + " wait 开始" + System.currentTimeMillis());
                waitNotify05.wait();
                System.out.println(Thread.currentThread().getName() + " wait 结束" + System.currentTimeMillis());
                System.out.println(Thread.currentThread().getName() + " s= " + waitNotify05.s);
            }

        } catch (Exception e) {
        }
    }
}  

线程二,主要是 notify 方法演示, 并修改字符串的值。

 class ThreadB extends Thread {
    private WaitNotify05 waitNotify05;

    public ThreadB(WaitNotify05 waitNotify05) {
        this.waitNotify05 = waitNotify05;
    }

    @Override
    public void run() {
        synchronized (waitNotify05) {
            System.out.println(Thread.currentThread().getName() + " notify 开始" + System.currentTimeMillis());
            waitNotify05.s = "notify()";
            waitNotify05.notify();
            System.out.println(Thread.currentThread().getName() + " notify 结束" + System.currentTimeMillis());
        }
    }
}  

测试方法:

 import java.util.concurrent.TimeUnit;

public class WaitNotify05 {
    public String s = "wait";

    public static void main(String[] args) throws InterruptedException {
        WaitNotify05 waitNotify05 = new WaitNotify05();
        ThreadA threadA = new ThreadA(waitNotify05);
        threadA.start();
        TimeUnit.SECONDS.sleep(3);

        ThreadB threadB = new ThreadB(waitNotify05);
        threadB.start();
    }
}  

完整代码和运行效果如下:

测试方法中等待了 3 秒。

还记得第一个小例子吗?我们来看看使用 wait / notify 如何实现。

更改以后的代码:

运行效果如下:

需要注意的是, 发起通知的时候, 并没有立即执行 wait 的方法,是因为 ThreadA 中的同步锁还没有释放。

并且还可以看到 wait 的一个特性,就是会理解释放锁。这个和 notify 方法不同, notify 方法, 会等待着锁执行完毕。根据上面的方法就可以看出来。 等着着 for 执行结束才开始执行。

同样还有一个方法也不释放锁, 就是 sleep() 方法。

wait 和 interrupt 方法引发的异常

这里的 wait 方法,还需要注意一个问题。就是和 interrupt() 方法共同使用的时候, 会出现 InterruptedException 异常。 这也可以认为是处理中断的一种方式。因为 stop 非安全性的而且还可能会出现错误的问题。

演示代码如下:

线程等待 3 秒以后,发起中断, 之后就出现了异常信息。

以上的方法都是一个线程的情况下,等待和通知都是一个,也就是 notify 的通知也是一个一个的通知。 如果有多个线程该怎么办呢? 可以使用 notifyAll() 方法。

这里就不演示了。 可以使用数组的方法, 多启动几个线程。查看其中的问题。

关注我。后续这些细节知识会在进行迭代。感谢您的阅读。

收藏,关注,点赞。 离过年越来越近了。 大家都能回家吗?还是就地过年了呢?提前祝大家新年快乐。

文章来源:智云一二三科技

文章标题:Java 17 线程的通信 wait/notify 机制

文章地址:https://www.zhihuclub.com/169277.shtml

关于作者: 智云科技

热门文章

网站地图