Java线程之间通信方式

news2025/7/19 4:12:53

目录

  • 1 线程之间的通信方式主要有以下几种
  • 2 共享变量
  • 3 锁机制
  • 4 条件变量
  • 5 信号量
  • 6 管道


1 线程之间的通信方式主要有以下几种

在这里插入图片描述

在实际开发时,一个进程中往往有很多个线程,大多数线程之间往往不是绝对独立的,比如说我们需要将A和B 两个线程的执行结果收集在一起然后显示在界面上,又或者比较典型的消费者-生产者模式,在这些场景下,线程间通信成了我们必须使用的手段,那么线程之间怎么通信呢?

线程间通信方式,从实现本质来讲,主要可以分为两大类共享内存和消息传递。

相信大家还记得,在内存模型一节,我们提到多线程并发情况下的三大特性,原子性,有序性,可见性,其所对应的解决方案就可以用来实现线程间通信,这些解决方案的本质就是共享内存。

对于消息传递而言,最经典的实现就是我们的Handler机制,在子线程使用主线程的Handler对象将一些信息发送到主线程以便进行处理。

下面我们来看一些线程间通信的典型实现

2 共享变量

共享变量:线程之间可以通过共享变量来进行通信。不同的线程可以共享同一个变量,并在变量上进行读写操作。需要注意的是,共享变量可能会引发线程安全问题,需要通过同步机制来确保线程安全。

public class SharedData {
    private int value;
    public synchronized int getValue() { 
        return value; 
    }
    public synchronized void setValue(int value) { 
        this.value = value; 
    }
}

​ 在这个示例中,定义了一个共享数据类 SharedData,其中包含一个整型变量 value 和两个同步方法 getValue()setValue(),用于获取和设置变量的值。由于这两个方法都是同步的,因此多个线程可以安全地访问该变量。

public class SharedDataExample {
    public static void main(String[] args) throws InterruptedException {
        SharedData sharedData = new SharedData();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                sharedData.setValue(i);
                System.out.println(Thread.currentThread().getName() + " write " + sharedData.getValue());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + " read " + sharedData.getValue());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
    }
}

​ 在这个示例中,创建了两个线程分别用于读写共享数据 SharedData,多次执行该示例可以看到控制台输出表明两个线程在安全地访问共享变量。

结果如图:
在这里插入图片描述

3 锁机制

锁机制:锁机制是一种常用的线程同步机制,可以保证在同一时间只有一个线程能够访问共享资源。Java提供了多种锁类型,如 synchronized 关键字、ReentrantLock 类等。

public class LockExample {
    private static Lock lock = new ReentrantLock();
    private static int count = 0;
    private static void increase() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                increase();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                increase();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(count);
    }
}

​ 在这个示例中,使用了 Lock 接口和 ReentrantLock 类来对计数器进行同步,多次执行该示例可以看到最终输出的计数器值为 20000。

结果如图:
在这里插入图片描述

4 条件变量

条件变量:条件变量是一种线程间通信机制,它用于在一个共享资源上等待某个条件的成立。Java 提供了 Condition 接口来支持条件变量的实现,在使用 Condition 时需要先获取锁,然后调用 await() 方法等待条件成立,当条件成立时可以通过 signal() 或 signalAll() 方法唤醒等待该条件的线程。

public class ConditionExample {
    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    private static int count = 0;
    private static void await() throws InterruptedException {
        lock.lock();
        try {
            condition.await();
        } finally {
            lock.unlock();
        }
    }
    private static void signal() {
        lock.lock();
        try {
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count++;
                System.out.println(Thread.currentThread().getName() + " increase count to " + count);
                if (count == 5) {
                    signal();
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            try {
                await();
                System.out.println(Thread.currentThread().getName() + " receive signal, count is " + count);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
    }
}

​ 在这个示例中,使用了 Lock 接口和 Condition 接口来定义了一个计数器,线程1每次增加计数器的值并判断是否达到条件,当计数器达到条件时调用 signal() 方法通知线程2,线程2等待条件成立后执行相应的操作。

在这里插入图片描述

5 信号量

信号量:信号量是一种常见的线程同步机制,可用于控制多个线程对共享资源的访问。Java 提供了 Semaphore 类来实现信号量,Semaphore 类有两个常用的方法 acquire() 和 release(),分别用于获取和释放信号量。

public class SemaphoreExample {
    private static Semaphore semaphore = new Semaphore(2);
    private static void doWork() throws InterruptedException {
        semaphore.acquire();
        System.out.println(Thread.currentThread().getName() + " start working");
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName() + " finish working");
        semaphore.release();
    }
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            try {
                doWork();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread2 = new Thread(() -> {
            try {
                doWork();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread3 = new Thread(() -> {
            try {
                doWork();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread1.start();
        thread2.start();
        thread3.start();
        thread1.join();
        thread2.join();
        thread3.join();
    }
}

​ 在这个示例中,使用了 Semaphore 类来定义了一个信号量,线程1、线程2、线程3都需要获取信号量才能进行工作,每次执行 doWork() 方法需要占用资源,执行完毕后释放信号量。

在这里插入图片描述

6 管道

管道:管道是一种用于线程间通信的高级机制,它可以实现一个线程向另一个线程传送数据。Java 提供了 PipedInputStream 和 PipedOutputStream 两个类来支持管道的实现,其中 PipedInputStream 用于读取数据,PipedOutputStream 用于写入数据。

public class PipeExample {
    static class WriterThread extends Thread {
        private PipedOutputStream output;
        WriterThread(PipedOutputStream output) {
            this.output = output;
        }
        @Override
        public void run() {
            try {
                for(int i=1;i<=10;i++) {
                    output.write(i);
                    System.out.println("写入数据:" + i);
                    Thread.sleep(1000);
                }
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    output.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    static class ReaderThread extends Thread {
        private PipedInputStream input;
        ReaderThread(PipedInputStream input) {
            this.input = input;
        }
        @Override
        public void run() {
            try {
                int value;
                while((value=input.read()) != -1) {
                    System.out.println("读取数据:" + value);
                }
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    input.close();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) throws IOException {
        PipedOutputStream output = new PipedOutputStream();
        PipedInputStream input = new PipedInputStream(output);
        Thread thread1 = new WriterThread(output);
        Thread thread2 = new ReaderThread(input);
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这个示例中,使用了 PipedOutputStream 类和 PipedInputStream 类来定义了一个管道,线程1向管道中写入数据,线程2从管道中读取数据,通过管道来实现两个线程之间的通信。

运行结果:在这里插入图片描述

需要注意的是,以上通信方式都需要在多线程程序中谨慎使用,需要考虑线程安全和性能等方面的问题。为了确保程序正确、高效地运行,需要根据具体情况选择合适的线程通信方式,并进行相应的测试和优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/995835.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C3d,C4d,C5d;

cl08267: ISOPREN_C2_like Superfamily

HBase 记录

HBase 管理命令 hbase hbck -details TABLE_NAME hbase hbck -repair TABLE_NAMEHBase概览 Master、RegionServer作用 RegionServer与Region关系 数据定位原理 https://blogs.apache.org/hbase/entry/hbase_who_needs_a_master RegionServer HBase Essentials.pdf (P25)…

解锁智慧照明新玩法,Construlita携手涂鸦智能打造创新方案!

近日&#xff0c;墨西哥头部照明品牌Construlita Lighting International SA DE CV&#xff08;以下简称&#xff1a;Construlita&#xff09;与全球化IoT开发者平台涂鸦智能&#xff08;NYSE: TUYA&#xff0c;HKEX: 2391&#xff09;在Construlita Connect发布会上宣布达成合…

Stable Diffusion 告别猜关键词,LoRA适配关键词自动生成

有没有想想过在SD绘图的时候下载好的LoRA模型选择之后不生效是为什么?或者说关键词不知道怎么填写? 这里介绍基于 Civitai 的LoRA 使用方法。 文章目录 Civitai 插件使用方法Civitai 插件 如果没有安装的小伙伴可以参考前面的文章先对 Civitai 的模型管理进行安装和使用,确…

龙芯指令集LoongArch——学习笔记(1)

1 龙芯架构 PDF下载链接&#xff1a; https://www.loongson.cn/download/index 1.1 龙芯架构概述 龙芯架构具有 RISC 指令架构的典型特征。 它的指令长度固定且编码格式规整&#xff0c; 绝大多数指令只有两个源操作数和一个目的操作数&#xff0c; 采用 load/store 架构&…

代码随想录算法训练营day42 | 动态规划 背包问题 01背包 二维数组一维数组 |416. 分割等和子集

动态规划&#xff1a;背包理论 背包理论基础 对于面试的话&#xff0c;其实掌握01背包&#xff0c;和完全背包&#xff0c;就够用了&#xff0c;最多可以再来一个多重背包。 如果这几种背包&#xff0c;分不清&#xff0c;我这里画了一个图&#xff0c;如下&#xff1a; 而完…

Unity 从0开始编写一个技能编辑器_01_分析需求

入职以来一直很想实现一个技能编辑器&#xff0c;在积累了一些经验以后&#xff0c;决定利用ScriptableObject开发一个&#xff0c;在此记录 1.简单的需求分析 在游戏开发中&#xff0c;技能系统是一个至关重要的组成部分。技能决定了游戏角色可以执行的各种动作&#xff0c;例…

【深入理解Linux内核锁】八、完成量

我的圈子: 高级工程师聚集地 我是董哥,高级嵌入式软件开发工程师,从事嵌入式Linux驱动开发和系统开发,曾就职于世界500强企业! 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 文章目录 1、完成量API2、API实现2.1 completion2.2 init_completion2.3 wait_for_com…

运行速度终于变快了!优化VMD参数,五种适应度函数任意切换,最小包络熵、样本熵、信息熵、排列熵、排列熵/互信息熵...

经常有小伙伴后台留言&#xff0c;说优化VMD参数的程序为什么运行时间辣么长&#xff0c;有没有办法改善一下&#xff01; 今天小淘就为大家带来了改善运行时间的方法&#xff1a;采用官方自带的vmd函数 这是MATLAB官方自带的VMD函数截图&#xff0c;可以看到只有2020a版本以后…

【JAVA】 图书管理系统(javaSE简易版 内含画图分析) | 期末大作业课程设计

作者主页&#xff1a;paper jie 的博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVA》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&…

SLAM从入门到精通(ROS安装)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 ROS科研上面用的多&#xff0c;实际生产其实用的也不少。它本身还是很好的应用框架。当然&#xff0c;它对于很多初学的同学来说还是很友好的。学完…

管理类联考——数学——汇总篇——知识点突破——工程

⛲️ 工程问题是应用题中仅次于路程问题的一个常考点&#xff0c;既是重点&#xff0c;也是难点。其主要的基本关系式为&#xff1a; 工作时间 工作效率 工作量 工作时间工作效率工作量 工作时间工作效率工作量。 本专题主要学习复杂的工程问题&#xff0c;主要有以下三种方…

【动手学深度学习】--机器翻译与数据集

文章目录 机器翻译与数据集1.下载和预处理数据集2.词元化3.词表4.加载数据集5.训练模型 机器翻译与数据集 学习视频&#xff1a;机器翻译数据集【动手学深度学习v2】 官方笔记&#xff1a;机器翻译与数据集 机器翻译&#xff08;machine translation&#xff09;指的是 将序…

absolute和relative元素层级问题

布局层级问题 同一个父元素的两个子元素&#xff0c;一个是absolute布局&#xff0c;一个是relative布局&#xff0c;为啥relative元素在absolute元素的背景色之上&#xff1f; 实例&#xff1a; <div id"father">father<div id"c1">c1-absolu…

人工智能海洋中的塞壬之歌:大型语言模型LLM中的幻觉研究综述(一)

“ 大型语言模型LLM 如 ChatGPT 已经得到了广泛的应用&#xff0c;涵盖了诸多应用领域。但也存在生成内容与事实不符的幻觉问题。这种幻觉包括输入冲突、语境冲突以及与事实相冲突的情况&#xff0c;给实际需求和应用构建带来了挑战。一篇最新的论文《Sirens Song in the AI Oc…

科技革新自动驾驶:拓世AI智能助理携手跟您一起点亮未来之旅

科技改变生活&#xff0c;智能改变世界&#xff0c;近年来&#xff0c;随着科技的不断进步&#xff0c;政策和市场的赋能推动&#xff0c;自动驾驶已经成为当今社会最炙手可热的话题之一。从其中的技术发展趋势来看&#xff0c;我国自动驾驶模式正由单车智能向车路协同时代演进…

王道数据结构编程题 栈和队列

判断合法序列 题目描述 假设 I 和 O 分别表示入栈和出栈操作&#xff0c;栈的初态和终态均为空&#xff0c;入栈和出栈的操作序列可表示为仅由 I 和 O 组成的序列&#xff0c;可以操作的序列称为合法序列&#xff0c;否则称为非法序列。编写一个算法&#xff0c;判定所给的序…

《向量数据库》——都有哪些向量数据库,都有什么特点?

随着大数据和机器学习应用的崛起,对于存储和查询高维度向量数据的需求也逐渐增加。在这个背景下,向量数据库和传统的关系型数据库开始在不同的数据世界中崭露头角,它们分别以各自独特的方式解决了不同类型数据的存储和查询需求。本文将探讨向量数据库和关系型数据库之间的区…

第5章_瑞萨MCU零基础入门系列教程之GPIO输入输出

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…