多线程环境下,线程通讯的方式主要采用两种方式,一种是内存共享型,一种是消息传递型。

内存共享

使用这种方式进行线程通讯,通常会设置一个共享变量,多个线程操作同一个共享变量,来达到多线程之间通讯的目的。这种方式需要考虑多线程之前的同步问题,这个共享变量必须通过加锁或同步的方式进行保护,一般采用sychronized、volatile关键字进行修饰。但共享变量多的情况下,易发生死锁问题。

消息传递

一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作,整个 过程开始于一个线程,而最终执行又是另一个线程。前者是生产者,后者就是消费者。

Java通过内置的等待/通知机制很好的实现了这个功能。等待/通知的相关方法是任意Java对象都具备的,因为这些方法被定义在所有对象的超类java.lang.Object上。

等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。

等待方遵循如下原则:

  1. 获取对象的锁;
  2. 如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件;
  3. 条件满足则执行对应的逻辑;

对应伪代码如下:

synchronized(对象) {
  while(条件不满足) {
     对象.wait();
  }
  对应的处理逻辑
}

通知方遵循如下原则:

  1. 获得对象的锁;
  2. 改变条件;
  3. 通知所有等待在对象上的线程;

对应的伪代码如下:

synchronized(对象){
   改变条件
   对象.notifyAll(); 
}

举例如下:

线程t1,t2分别循环打印0和1,一个递增,一个递减,

class Scratch {

    public static void main(String[] args) {
        WaitNotifyTest waitNotifyTest =new WaitNotifyTest();
        Thread t1 = new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    waitNotifyTest.increase();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");

        Thread t2 = new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    waitNotifyTest.decrease();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2");

        t1.start();
        t2.start();
    }
}

class WaitNotifyTest{

    private int value = 0;
    public synchronized void increase() throws InterruptedException {
        while (value != 0) {
            wait();
        }
        value++;

        System.out.println("线程-"+Thread.currentThread().getName()+" value-"+value);
        notifyAll();
    }

    public synchronized void decrease() throws InterruptedException {
        while (value != 1) {
            wait();
        }
        value--;
        System.out.println("线程-"+Thread.currentThread().getName()+" value-"+value);
        notifyAll();
    }
}

输出如下:

线程-t1 value-1
线程-t2 value-0
线程-t1 value-1
线程-t2 value-0
线程-t1 value-1
线程-t2 value-0
线程-t1 value-1
......

posted on   misterD  阅读(138)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了



点击右上角即可分享
微信分享提示