学习笔记12——并发编程之线程之间协作方式

线程之间协作有哪些方式

当多个线程可以一起工作去解决某个问题时,如果某些部分必须在其他部分之前完成,那么就需要对线程进行协调。

共享变量和轮询方式

实现:定义一个共享变量(如 volatile 修饰的布尔标志)。线程通过检查共享变量的状态来决定是否继续执行。

public class Test {
​
    private static volatile boolean flag = false;
​
    public static void main(String[] args) throws InterruptedException {
​
        new Thread(new Runnable() {
            public void run() {
                System.out.println("flag" + flag);
                while (!flag){
                    System.out.println( "waiting"); //轮询等待
                }
                System.out.println("Flag is now " + flag);
            }
        }).start();
​
        Thread.sleep(1000);
        flag = true; // 修改共享变量
    }
}

使用wait() 与 notify()/notifyAll()

通过object类中的wait()、notify()和notifyAll来实现。

实现:

  • wait():当前线程释放锁并进入等待状态(WAITING)。需在同步块中调用(持有锁)。

  • notify():随机唤醒一个等待线程(WAITINGBLOCKED)。

  • notifyAll():唤醒所有等待线程。

public class ProducerConsumer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 10;
    public void produce(int value) throws InterruptedException {
        synchronized (queue) {
            while (queue.size() == capacity) {
                queue.wait(); // 队列满,等待
            }
            queue.add(value);
            queue.notifyAll(); // 唤醒消费者
        }
    }
    public int consume() throws InterruptedException {
        synchronized (queue) {
            while (queue.isEmpty()) {
                queue.wait(); // 队列空,等待
            }
            int value = queue.poll();
            queue.notifyAll(); // 唤醒生产者
            return value;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ProducerConsumer pc = new ProducerConsumer();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    pc.produce(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
​
        new Thread(() -> {
            while (true){
                try {
                    System.out.println(pc.consume());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
}
​

 Condition 条件变量

通过 ReentrantLockCondition 实现更灵活的线程协作。

实现:Condition类似于 wait()/notify(),但支持多个条件队列,使用它需要和ReentrantLock配合使用。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
​
public class ProducerConsumerWithCondition {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 10;
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
​
    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                notFull.await(); // 等待队列不满
            }
            queue.add(value);
            notEmpty.signalAll(); // 唤醒消费者
        } finally {
            lock.unlock();
        }
    }
​
    public int consume() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await(); //等待队列不空
            }
            int value = queue.poll();
            notFull.signalAll(); // 唤醒生产者
            return value;
        } finally {
            lock.unlock();
        }
    }
}

CountDownLatch

通过CountDownLatch实现线程的等待与唤醒。

实现:初始化时指定计数值,线程调用await()等待计数器归零;其他线程调用countDown()减少计数器。

import java.util.concurrent.CountDownLatch;
​
public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 3;
        CountDownLatch latch = new CountDownLatch(threadCount);
​
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                System.out.println("Thread finished");
                latch.countDown(); // 计数器减1
            }).start();
        }
​
        latch.await(); // 等待所有线程完成
        System.out.println("All threads finished");
    }
}

CyclicBarrier

是一个同步辅助工具,允许一组线程相互等待,直到所有线程到达屏障点后才能继续执行。可以重复使用。

实现:初始化时指定参与线程数;线程调用await()等待其他线程到达屏障点;所有线程到达后,屏障重置,可重复使用。

import java.util.concurrent.CyclicBarrier;
​
public class CyclicBarrierExample {
    public static void main(String[] args) {
        int threadCount = 3;
        CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
            System.out.println("All threads reached the barrier");
        });
​
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                System.out.println("Thread waiting at barrier");
                try {
                    barrier.await(); // 等待其他线程
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Semaphore信号量

通过Semaphore控制资源的并发访问。

实现:初始化时指定许可数,线程调用acquire()获取许可,调用release()释放许可。

import java.util.concurrent.Semaphore;
​
public class SemaphoreExample {
    public static void main(String[] args) {
        int permits = 2;
        Semaphore semaphore = new Semaphore(permits);
​
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    System.out.println("Thread acquired permit");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放许可
                    System.out.println("Thread released permit");
                }
            }).start();
        }
    }
}

阻塞队列

阻塞队列(Blocking Queue) 是 Java 多线程中实现线程协作的核心工具之一。它通过内置的阻塞机制,让生产者线程和消费者线程自动协调工作,无需开发者手动管理 wait()notify() 或锁的细节。

阻塞队列是一种特殊的队列,提供以下功能:

  1. 队列空时的阻塞:消费者线程尝试从空队列取数据时,会被阻塞直到队列非空。

  2. 队列满时的阻塞:生产者线程尝试向满队列放数据时,会被阻塞直到队列有空位。

  3. 支持超时机制:部分阻塞队列允许在指定时间内尝试操作,超时后返回失败

Java 并发包 java.util.concurrent 提供了多种阻塞队列实现:

实现类特点
ArrayBlockingQueue基于数组的有界队列,固定容量,公平性可选。
LinkedBlockingQueue基于链表的队列,默认无界(可指定容量),吞吐量高。
PriorityBlockingQueue支持优先级排序的无界队列。
SynchronousQueue不存储元素的队列,生产者插入操作必须等待消费者移除。
DelayQueue元素按延迟时间排序,只有到期后才能被取出。

示例:生产者——消费者模型

通过阻塞队列,生产者和消费者线程无需直接交互,只需要操作队列即可自动协调;

  • 生产者线程:调用 put() 方法放入数据,若队列满则阻塞。

  • 消费者线程:调用 take() 方法取出数据,若队列空则阻塞。

协作流程

  • 生产者放数据

    • 若队列未满,直接插入数据并唤醒消费者线程。

    • 若队列已满,生产者线程阻塞,等待消费者取走数据后唤醒。

  • 消费者取数据

    • 若队列非空,直接取出数据并唤醒生产者线程。

    • 若队列为空,消费者线程阻塞,等待生产者放入数据后唤醒。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
​
public class ProducerConsumerExample {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); // 容量为10的阻塞队列
​
        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 100; i++) {
                    queue.put(i); // 队列满时自动阻塞
                    System.out.println("Produced: " + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
​
        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    Integer value = queue.take(); // 队列空时自动阻塞
                    System.out.println("Consumed: " + value);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
​
        producer.start();
        consumer.start();
    }
}

总结

协作方式适用场景优点缺点
共享变量与轮询简单状态检查简单易用浪费 CPU,无法精确唤醒
wait/notify生产者-消费者模型精确控制线程唤醒需手动管理锁和条件
Condition复杂条件等待支持多条件队列,灵活性高代码复杂度高
CountDownLatch一次性等待场景(如任务完成)简单易用不可重置
CyclicBarrier分阶段任务同步可重复使用需预先知道线程数
Semaphore控制资源并发访问灵活控制并发数需手动管理许可
阻塞队列生产者-消费者模型线程池任务队列:如 ThreadPoolExecutor 使用阻塞队列管理待执行任务。 流量控制:通过有界队列限制系统资源使用,防止内存溢出。 延迟任务调度:如 DelayQueue 实现定时任务执行。简化代码避免竞态条件,自动阻塞与唤醒,支持多种策略无界队列可能导致内存溢出。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker基础篇——Ubuntu下Docker安装

大家好我是木木&#xff0c;在当今快速发展的云计算与云原生时代&#xff0c;容器化技术蓬勃兴起&#xff0c;Docker 作为实现容器化的主流工具之一&#xff0c;为开发者和运维人员带来了极大的便捷 。下面我们一起进行Docker安装。 Docker的官方Ubuntu安装文档&#xff0c;如…

Python图形编程之EasyGUI: indexbox的用法

目录<<上一章&#xff1a;ynbox用法详解 下一章&#xff1a;boolbox用法详解 >> # 1 Python图形编程之EasyGUI: indexbox的用法 1.1 基本用法 indexbox提供用户一个选择不同选项的功能&#xff0c;不同的选项由按钮来表示&#xff0c;提供类似功能的还有choicebox…

大语言模型从理论到实践(第二版)-学习笔记(绪论)

大语言模型的基本概念 1.理解语言是人工智能算法获取知识的前提 2.语言模型的目标就是对自然语言的概率分布建模 3.词汇表 V 上的语言模型&#xff0c;由函数 P(w1w2 wm) 表示&#xff0c;可以形式化地构建为词序列 w1w2 wm 的概率分布&#xff0c;表示词序列 w1w2 wm…

突破极限!蓝耘通义万相2.1引爆AI多模态新纪元——性能与应用全方位革新

云边有个稻草人-CSDN博客 目录 一、 引言 二、 蓝耘通义万相2.1版本概述 三、 蓝耘通义万相2.1的核心技术改进 【多模态数据处理】 【语音识别与文本转化】 【自然语言处理&#xff08;NLP&#xff09;改进】 【跨平台兼容性】 四、 蓝耘注册 部署流程—新手也能轻松…

JVM常用概念之本地内存跟踪

问题 Java应用启动或者运行过程中报“内存不足&#xff01;”&#xff0c;我们该怎么办? 基础知识 对于一个在本地机器运行的JVM应用而言&#xff0c;需要足够的内存来存储机器代码、堆元数据、类元数据、内存分析等数据结构&#xff0c;来保证JVM应用的成功启动以及未来平…

p5.js:sound(音乐)可视化,动画显示音频高低变化

本文通过4个案例介绍了使用 p5.js 进行音乐可视化的实践&#xff0c;包括将音频振幅转化为图形、生成波形图。 承上一篇&#xff1a;vite&#xff1a;初学 p5.js demo 画圆圈 cd p5-demo copy .\node_modules\p5\lib\p5.min.js . copy .\node_modules\p5\lib\addons\p5.soun…

PDF处理控件Aspose.PDF,如何实现企业级PDF处理

PDF处理为何成为开发者的“隐形雷区”&#xff1f; “手动调整200页PDF目录耗时3天&#xff0c;扫描件文字识别错误导致数据混乱&#xff0c;跨平台渲染格式崩坏引发客户投诉……” 作为开发者&#xff0c;你是否也在为PDF处理的复杂细节消耗大量精力&#xff1f;Aspose.PDF凭…

ruo-yi项目启动备忘

ruo-yi项目启动遇到问题备忘 参考文档: 若依 手把手启动 https://blog.csdn.net/qq_43804008/article/details/132950644?utm_mediumdistribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1-132950644-blog-137337537.235^v43^pc_blog_bottom_…

⭐LeetCode周赛 Q1. 找出最大的几近缺失整数——模拟⭐

⭐LeetCode周赛 Q1. 找出最大的几近缺失整数——模拟⭐ 示例 1&#xff1a; 输入&#xff1a;nums [3,9,2,1,7], k 3 输出&#xff1a;7 解释&#xff1a; 1 出现在两个大小为 3 的子数组中&#xff1a;[9, 2, 1]、[2, 1, 7] 2 出现在三个大小为 3 的子数组中&#xff1a;[3,…

Java 集合框架大师课:性能调优火葬场(四)

&#x1f680; Java 集合框架大师课&#xff1a;性能调优火葬场&#xff08;四&#xff09; &#x1f525; 战力值突破 95% 警告&#xff01;调优就像吃重庆火锅——要选对食材&#xff08;数据结构&#xff09;还要控制火候&#xff08;算法&#xff09;&#x1f336;️ 第一章…

【愚公系列】《Python网络爬虫从入门到精通》045-Charles的SSL证书的安装

标题详情作者简介愚公搬代码头衔华为云特约编辑&#xff0c;华为云云享专家&#xff0c;华为开发者专家&#xff0c;华为产品云测专家&#xff0c;CSDN博客专家&#xff0c;CSDN商业化专家&#xff0c;阿里云专家博主&#xff0c;阿里云签约作者&#xff0c;腾讯云优秀博主&…

蓝桥杯嵌入式组第七届省赛题目解析+STM32G431RBT6实现源码

文章目录 1.题目解析1.1 分而治之&#xff0c;藕断丝连1.2 模块化思维导图1.3 模块解析1.3.1 KEY模块1.3.2 ADC模块1.3.3 IIC模块1.3.4 UART模块1.3.5 LCD模块1.3.6 LED模块1.3.7 TIM模块 2.源码3.第七届题目 前言&#xff1a;STM32G431RBT6实现嵌入式组第七届题目解析源码&…

KUKA机器人:智能制造的先锋力量

在科技日新月异的今天&#xff0c;自动化和智能化已成为推动制造业转型升级的重要引擎。作为全球领先的智能、资源节约型自动化解决方案供应商&#xff0c;KUKA机器人在这一浪潮中扮演着举足轻重的角色。本文将带您深入了解KUKA机器人的发展现状&#xff0c;探索其在智能制造领…

Ateme在云端构建可扩展视频流播平台

Akamai Connected Cloud帮助Ateme客户向全球观众分发最高质量视频内容。 “付费电视运营商和内容提供商现在可以在Akamai Connected Cloud上通过高质量视频吸引观众&#xff0c;并轻松扩展。”── Ateme首席战略官Rmi Beaudouin ​ Ateme是全球领先的视频压缩和传输解决方案提…

OceanBase社区年度之星专访:张稚京与OB社区的双向奔赴

2024年年底&#xff0c;OceanBase社区颁发了“年度之星”奖项&#xff0c;旨在表彰过去一年中为 OceanBase 社区发展作出卓越贡献的个人。今天&#xff0c;我们有幸邀请到这一荣誉的获得者——来自融科智联的张稚京老师&#xff0c;并对他进行了专访。 在过去的一年中&#xf…

如何选择国产串口屏?

目录 1、迪文 2、淘晶驰 3、广州大彩 4、金玺智控 5、欣瑞达 6、富莱新 7、冠显 8、有彩 串口屏&#xff0c;顾名思义&#xff0c;就是通过串口通信接口&#xff08;如RS232、RS485、TTL UART等&#xff09;与主控设备进行通信的显示屏。其核心功能是显示信息和接收输入…

涨薪技术|Kubernetes(k8s)之Service服务

01Service简介 Kubernetes Pod 是有生命周期的&#xff0c;它们可以被创建&#xff0c;也可以被销毁&#xff0c;然而一旦被销毁生命就永远结束。通过 ReplicationController 能够动态地创建和销毁 Pod&#xff08;例如&#xff0c;需要进行扩缩容&#xff0c;或者执行 滚动升…

Quickwit+Jaeger+Prometheus+Grafana搭建Java日志管理平台

介绍 生产服务应用可观测性在当下比较流行的方案&#xff0c;其中出现了大量高性能、开箱即用、易上手的的开源产品&#xff0c;大大丰富了在可观测性领域产品的多样性&#xff0c;本文讲述基于OTLP协议推送Java项目遥测数据&#xff08;日志、指标、链路&#xff09;到后端存储…

「mysql」Mac mysql一路畅通式安装

折腾了一上午&#xff0c;遇到的各种错误&#xff1a; 错误一&#xff1a;安装后&#xff0c;终端执行 mysql 或者执行 mysql -u root -p 时报错&#xff1a; ERROR 1045 (28000): Access denied for user rootlocalhost (using password: YES)错误二&#xff1a;为解决错误一&…

Linux原生异步IO原理与实现(Native AIO)

异步 IO&#xff1a;当应用程序发起一个 IO 操作后&#xff0c;调用者不能立刻得到结果&#xff0c;而是在内核完成 IO 操作后&#xff0c;通过信号或回调来通知调用者。 异步 IO 与同步 IO 的区别如图所示&#xff1a; 从上图可知&#xff0c;同步 IO 必须等待内核把 IO 操作处…