• Java并发编程——线程间通信


    一、volatile 关键字

    为什么volatile关键字可以?因为之前说过了,此关键字能保证变量的可见性,也就是说变量一旦被修改,立马能被其他线程所感知

    例子如下:

    private static volatile boolean flag=true;
    
    private static void setFlag(){
        flag=false;
    }
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println("还没变");
            while (flag){
                // flag不改变 就一直死循环直至发现值改变
            }
            System.out.println("我感知到变化了");
        }).start();
        try {
            //等待1s才执行下一个线程
            Thread.sleep(1000); 
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            setFlag();
            System.out.println("我修改了");
        }).start();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    二、等待/通知机制

    相关方法为:

    • notify():通知一个在对象上等待的线程,使其从wait()方法返回,返回的前提是该线程获得了锁
    • notifyAll():通知所有在该对象上等待的线程
    • wait():调用该方法进入waiting状态并会释放锁,只有被其他线程通知或者中断才返回
    • wait(long):超时等待一段时间,到时间了还没通知就返回
    • wait(long,int):增加了超时时间单位的控制

    例子如下:

    public static Object flag=true;
    
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            synchronized (flag){
                System.out.println("线程1:进入等待");
                try {
                    flag.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1:从等待中归来");
            }
        }).start();
       Thread.sleep(1000);
       new Thread(()->{
           synchronized (flag){
               System.out.println("线程2:上面那个线程调用wait后会释放锁,所以到我啦");
               flag.notify();
               System.out.println("线程2:我把它唤醒了,但是它需要等我执行完释放掉锁才会返回");
           }
       }).start();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    结果:

    在这里插入图片描述

    注意:这个使用需要配合synchronized 使用

    三、管道通信

    管道输入流/输出流主要包括如下4个具体实现:

    • PipedInputStream
    • PipedOutputStream
    • PipedReader
    • PipedWriter

    前两种面向字节,后两种面向字符

    例子如下:

    public static void main(String[] args) throws InterruptedException, IOException {
            // 读为接收
            PipedReader in = new PipedReader();
            // 写为输出
            PipedWriter out = new PipedWriter();
            // 将管道输入和输出连接起来
            out.connect(in);
            new Thread(() -> {
                int receive = 0;
                while (true) {
                    try {
                        if (((receive = in.read()) != -1)) {
                            System.out.println("接收到了:"+receive);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            new Thread(()->{
                try {
                    // 这里输入,给那边接收
                    out.write(11111);
                    // 死循环防止输入端结束 管道断裂
                    while (true){}
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    结果:

    在这里插入图片描述

    四、Thread.join

    这个是干嘛呢?就是等待线程结束

    假设线程A执行了threadB.join()语句,其含义是:当前线程A等待线程B执行结束后才返回。说白了就是等待其他线程执行后才执行

    例子如下:

    public static void main(String[] args) throws InterruptedException, IOException {
          System.out.println("主线程:我开始执行啦");
          Thread threadB = new Thread(() -> {
              System.out.println("线程B:我开始执行啦");
              System.out.println("线程B:我要睡会儿");
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("线程B:我睡醒执行完了");
          });
          threadB.start();
          System.out.println("主线程:我等待线程B执行完我再执行");
          threadB.join();
          System.out.println("主线程:线程B执行完了,到我执行了");
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    结果:主线程一定是最后才执行完的

    在这里插入图片描述

    但是如果把join注释掉,那么线程B一定是最后执行完的:

    在这里插入图片描述

  • 相关阅读:
    groupadd
    【Leetcode】剑指Offer 29:顺时针打印矩阵
    《进阶篇第9章》学习vuex知识点后练习:把求和案例改成vuex版代码
    大语言模型底层架构丨带你认识Transformer
    02- 数据结构与算法 - 最长回文子串(动态规划/中心扩展算法/Manacher 算法)
    Dubbo后台管理和监控中心部署
    FPGA-结合协议时序实现UART收发器(五):串口顶层模块UART_TOP、例化PLL、UART_FIFO、uart_drive
    [短记] speechbrain
    系统架构师--面向对象选择题
    使用命令行方式搭建uni-app + Vue3 + Typescript + Pinia + Vite + Tailwind CSS + uv-ui开发脚手架
  • 原文地址:https://blog.csdn.net/weixin_44102992/article/details/127658846