【Java多线程】4——特定场景解决办法

4 特定场景解决方法

⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
笔记仓库👉https://github.com/A-BigTree/tree-learning-notes
个人主页👉https://www.abigtree.top
⭐⭐⭐⭐⭐⭐


如果可以,麻烦各位看官顺手点个star~😊

如果文章对你有所帮助,可以点赞👍收藏⭐支持一下博主~😆


文章目录

  • 4 特定场景解决方法
    • 4.1 `CountDownLatch`
      • 实例
    • 4.2 `CyclicBarrier`
      • 实例
    • 4.3 `Semaphore`
      • 4.3.1 常规方式使用
      • 4.3.2 引入超时机制
      • 4.3.3 应用场景举例
    • 4.4 `Fork Join`
      • 4.4.1 介绍
      • 4.4.2 API介绍
        • `RescursiveTask`
        • `ForkJoinTask`
      • 4.4.3 案例
        • 需求
        • 代码

4.1 CountDownLatch

效果:指定一个操作步骤数量,在各个子线程中,每完成一个任务就给步骤数量 - 1;在步骤数量减到0之前,CountDownLatch 可以帮我们把最后一步操作抑制住(阻塞),让最后一步操作一直等到步骤被减到 0 的时候执行。

实例

有六名同学在值日,班长负责锁门。班长必须确保所有同学都离开教室再锁门。

// 声明一个变量,用来保存同学的数量
int stuNum = 6;

// 创建CountDownLatch对象
CountDownLatch countDownLatch = new CountDownLatch(stuNum);

// 创建和同学数量相等的线程
for (int i = 0; i < stuNum; i++) {

    String num = String.valueOf(i + 1);

    new Thread(()->{

        // 完成一次操作
        System.out.println(Thread.currentThread().getName() + " " + num + "号同学离开教室");

        // 让countDownLatch管理的数量-1
        countDownLatch.countDown();

    }).start();

}

// 让countDownLatch负责将最后一步操作抑制住
countDownLatch.await();

System.out.println("班长锁门");

4.2 CyclicBarrier

支持多线程在执行各自任务的时候,到达某个状态点就等待,等所有线程都到达这个状态点再继续执行后步骤。

实例

public class DemoO19CyclicBarrierTest {

    private static List<List<String>> matrix = new ArrayList<>();

    static {
        matrix.add(Arrays.asList("normal","special","end"));
        matrix.add(Arrays.asList("normal","normal","special","end"));
        matrix.add(Arrays.asList("normal","normal","normal","special","end"));
    }

    public static void main(String[] args) {


        // 1.创建CyclicBarrier对象
        CyclicBarrier barrier = new CyclicBarrier(3);

        // 2.创建3个线程分别执行各自的任务
        new Thread(()->{

            try {
                List<String> list = matrix.get(0);

                for (String value : list) {

                    TimeUnit.SECONDS.sleep(1);

                    System.out.println(Thread.currentThread().getName() + " value = " + value);

                    if ("special".equals(value)) {

                        // 遇到特殊任务标记,就让当前线程等一下
                        barrier.await();
                    }

                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

        }, "thread01").start();

        new Thread(()->{

            try {
                List<String> list = matrix.get(1);

                for (String value : list) {

                    TimeUnit.SECONDS.sleep(1);

                    System.out.println(Thread.currentThread().getName() + " value = " + value);

                    if ("special".equals(value)) {

                        // 遇到特殊任务标记,就让当前线程等一下
                        barrier.await();
                    }

                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

        }, "thread02").start();
        new Thread(()->{

            try {
                List<String> list = matrix.get(2);

                for (String value : list) {

                    TimeUnit.SECONDS.sleep(1);

                    System.out.println(Thread.currentThread().getName() + " value = " + value);

                    if ("special".equals(value)) {

                        // 遇到特殊任务标记,就让当前线程等一下
                        barrier.await();
                    }

                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

        }, "thread03").start();
    }

}

4.3 Semaphore

4.3.1 常规方式使用

使用 Semaphore 可以帮助我们管理资源位;当某个线程申请资源时,由 Semaphore 检查这个资源是否可用;如果其他线程释放了这个资源,那么申请资源的线程就可以使用。

// 1、创建 Semaphore 对象,指定资源数量为 3
Semaphore semaphore = new Semaphore(3);

// 2、创建 10 个线程争夺这 3 个资源
for (int i = 0; i < 10; i++) {

    new Thread(() -> {

        try {
            // 申请资源
            semaphore.acquire();

            // 拿到资源执行操作
            System.out.println("【" + Thread.currentThread().getName() + "】号车辆【驶入】车位");

            TimeUnit.SECONDS.sleep(3);

            System.out.println("【" + Thread.currentThread().getName() + "】号车辆【驶出】车位");

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {

            // 操作完成释放资源
            semaphore.release();
        }

    }, i + "").start();
}

4.3.2 引入超时机制

// 1、设定车位数量
int carPositionCount = 3;

// 2、创建 Semaphore 对象
Semaphore semaphore = new Semaphore(carPositionCount);

// 3、创建 50 个线程抢车位
for (int i = 0; i < 50; i++) {

    int carNum = i;

    new Thread(()->{

        boolean acquireResult = false;

        try {

            // 线程开始时先申请资源,申请不到会进入等待状态
            // 申请资源方式一:不见不散,等不到资源就一直等
            // semaphore.acquire();

            // 申请资源方式二:过时不候
            acquireResult = semaphore.tryAcquire(3, TimeUnit.SECONDS);

            if (acquireResult) {
                // 申请到资源时,线程会继续执行
                System.out.println(carNum + "号车辆驶入车位");

                // 车辆在车位停放一段时间
                TimeUnit.SECONDS.sleep(2);

                // 停放完成离开车位
                System.out.println(carNum + "号车辆驶出车位");

            } else {

                System.out.println(carNum + "号车辆放弃等待");

            }


        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            // 判断当前线程释放拿到了资源
            if (acquireResult) {
                // 任务执行完成释放资源
                semaphore.release();

            }

        }

    }).start();

}    

4.3.3 应用场景举例

借助Semaphore实现『限流』操作。

  • 当前服务器实例能够承受多大的访问量——设置为资源的数量。
  • 根据资源的数量创建Semaphore对象。
  • 服务器实例接收到请求通过Semaphore对象管理处理请求数量。
    • 在能力范围内:处理请求。
    • 超过能力范围:设定等待时间,看是否能够得到别的请求处理完成释放资源。

4.4 Fork Join

4.4.1 介绍

使用 Fork Join 框架能够帮助我们把一个大型任务,根据一定规律,拆分成小任务执行。如果拆分后的任务还不够小,可以以递归模式继续拆分,直到拆分到可以执行的程度。然后再把各个子任务执行的结果汇总到一起。

  • Fork:拆分:把大任务拆分成小任务。
  • Join:合并:把小任务执行的结果合并到一起。

在这里插入图片描述

4.4.2 API介绍

RescursiveTask

在这里插入图片描述

我们使用 Fork Join 框架只需要继承 RecursiveTask,然后重写 compute() 方法即可。在 compute() 方法中需要包含:

  • 任务拆分的逻辑
  • 任务拆分的操作:调用 fork() 方法
  • 已拆分任务的合并:调用 join() 方法
  • 子任务结果的合并:将 join() 方法的返回值合并起来
ForkJoinTask

ForkJoinTask 类是 RecursiveTask 的父类。

在这里插入图片描述

4.4.3 案例

需求

完成从 1~100 的累加。

代码
// 任务类
class MyTask extends RecursiveTask {

    // 区间开始位置
    private int begin;

    // 区间结束位置
    private int end;

    // 区间调整值:要通过拆分任务将区间调整到 10 以内
    public static final int ADJUST_VALUE = 10;

    // 保存当前任务的结果
    private int result = 0;

    // 声明构造器,设定当前任务的开始和结束位置
    public MyTask(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    @Override
    protected Object compute() {

        // 1、判断当前区间是否是原子任务中可以执行计算的范围
        if (end - begin <= ADJUST_VALUE) {

            for (int i = begin; i <= end ; i++) {
                result  = result + i;
            }

        } else {

            // 2、计算新拆分任务的区间范围
            int leftBegin = begin;
            int leftEnd = (begin + end) / 2;

            int rightBegin = leftEnd + 1;
            int rightEnd = end;

            // 3、创建两个新的任务(子任务)
            MyTask myTaskLeft = new MyTask(leftBegin, leftEnd);
            MyTask myTaskRight = new MyTask(rightBegin, rightEnd);

            // 4、调用框架提供的 fork() 进一步拆分任务
            myTaskLeft.fork();
            myTaskRight.fork();

            // 5、调用框架提供的 join() 获取子任务计算的结果
            int leftResult = (int) myTaskLeft.join();
            int rightResult = (int) myTaskRight.join();

            // 6、把子任务的结果合并到一起
            result = leftResult + rightResult;
        }

        return result;
    }

}

// 测试代码
// 1、创建 Fork Join 任务池
ForkJoinPool pool = new ForkJoinPool();

// 2、创建任务对象
MyTask myTask = new MyTask(1, 100);

// 3、将任务对象提交到任务池
ForkJoinTask forkJoinTask = pool.submit(myTask);

// 4、获取任务执行结果
int finalResult = (int) forkJoinTask.get();

System.out.println("finalResult = " + finalResult);

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

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

相关文章

Linux: 检测工具: monit: cpu占用率的一个例子

最近看到monit使用的CPU有时候会突然的增加,通过perf查看,可以看到是下面这个函数调用占用的比较高。 Overhead Comma Shared Object Symbol 29.72% monit [kernel.kallsyms] [k] __d_lookup__d_lookup29.65% d_lookup proc_fill_cache ` proc_readfd_common iterate_dir ksy…

FPGA芯片在通信基站中的作用

基站作为移动通信系统的核心组成部分&#xff0c;承担着信号的发送和接收任务&#xff0c;包括天线、射频前端、数字信号处理和控制等功能。 随着通信技术不断进步和网络容量的提升&#xff0c;基站功能日益复杂&#xff0c;数量也在增加。 与此同时&#xff0c;FPGA芯片被广…

JZ-7-201XMT跳位合位监视专用继电器 220VDC 板后接线,面板安装 JOSEF约瑟

系列型号&#xff1a; JZ-7Y-201XMT跳位合位监视继电器&#xff1b; JZ-7J-201XMT跳位合位监视继电器&#xff1b; JZ-7Y-203XMT跳位合位监视继电器&#xff1b; JZ-7J-203XMT跳位合位监视继电器&#xff1b; JZ-7Y-204XMT跳位合位监视继电器&#xff1b; JZ-7J-204XMT跳…

上采样技术在语义分割中的应用

目录 概要 一、概述 二、实现方法 1.转置卷积 2.反池化 3.双线性插值法 三、在经典网络中的的应用 1.U-Net 2.FCN 总结 概要 上采样是用于深度学习中提高语义分割精度的技术&#xff0c;可以实现图像放大和像素级别标注 一、概述 神经网络的基本结构为&#xff1a;…

【讲解下go和java的区别】

&#x1f525;博主&#xff1a;程序员不想YY啊&#x1f525; &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f4ab; &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 &#x1f308;希望本文对您有所裨益&#xff0c;如有…

【深耕 Python】Data Science with Python 数据科学(2)jupyter-lab和numpy数组

关于数据科学环境的建立&#xff0c;可以参考我的博客&#xff1a;【深耕 Python】Data Science with Python 数据科学&#xff08;1&#xff09;环境搭建 Jupyter代码片段1&#xff1a;简单数组的定义和排序 import numpy as np np.array([1, 2, 3]) a np.array([9, 6, 2, …

vue3-pinia使用(末尾有彩蛋)

什么是 pinia Pinia 是 Vue 的专属状态管理库&#xff0c;它允许你跨组件或页面共享状态。 之前用的是 vuex&#xff0c;后面 vue 官方团队不维护了&#xff0c;推荐使用 pinia 安装 yarn add pinia # 或者使用 npm npm install piniapnpm install piniaStore 是什么&#xf…

2024 ccfcsp认证打卡 2022 06 01 归一化处理

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt(); // 输入数字的个数int[] a new int[1010]; // 创建一个数组来存储输入的数字double sum 0; // 用于计算所有输入数字的总…

COLMAP(Windows)实现SFM三维重建位姿估计

问题产生&#xff1a; Guassian splatting第一步用colmap进行位姿估计&#xff0c;图片匹配失败&#xff0c;输出图片全靠运气&#xff0c;最少的时候甚至一张都没匹配上&#xff0c;所以想到用colmap软件先进行匹配&#xff0c;再放入高斯训练。 colmap使用步骤&#xff1a;…

小米汽车引入革命性卫星通信技术:专利揭示直连卫星能力

小米汽车在近期的SU7发布会上&#xff0c;虽已展示了其运动轿跑车型的各项卓越性能&#xff0c;售价起于21.59万元&#xff0c;但其技术创新的深度远不止于此。一项最新公布的专利显示&#xff0c;小米汽车科技有限公司正在积极探索和开发车载卫星通信技术&#xff0c;该技术的…

后端常问面经之操作系统

请简要描述线程与进程的关系,区别及优缺点&#xff1f; 本质区别&#xff1a;进程是操作系统资源分配的基本单位&#xff0c;而线程是任务调度和执行的基本单位 在开销方面&#xff1a;每个进程都有独立的代码和数据空间&#xff08;程序上下文&#xff09;&#xff0c;程序之…

NSGA算法

先给自己叠甲&#xff0c;记录自己的学习过程&#xff0c;如有内容错误欢迎指正!!!。 1. NSGA算法简介&#xff08;Nondominated Sorting Genetic Algorithm&#xff09; 根据标题&#xff0c;NSGA算法分为两个要点&#xff0c;Nondominated Sorting&#xff08;非支配排序&a…

Golang实战:深入hash/crc64标准库的应用与技巧

Golang实战&#xff1a;深入hash/crc64标准库的应用与技巧 引言hash/crc64简介基本原理核心功能 环境准备安装Golang创建一个新的Golang项目引入hash/crc64包测试环境配置 hash/crc64的基本使用计算字符串的CRC64校验和计算文件的CRC64校验和 高级技巧与应用数据流和分块处理网…

springboot 使用@profiles.active@多配置文件切换

项目配置文件结构&#xff1a; 主配置文件内容&#xff1a; pom配置文件&#xff1a; <profiles><profile><id>dev</id><properties><profiles.active>dev</profiles.active></properties></profile><profile>…

鸿蒙OS开发实战:【Socket小试MQTT连接】

本篇分享一下 HarmonyOS 中的Socket使用方法 将从2个方面实践&#xff1a; HarmonyOS 手机应用连接PC端 SocketServerHarmonyOS 手机应用连接MQTT 服务端 通过循序渐进的方式&#xff0c;全面了解实践HarmonyOS中的Socket用法 学习本章前先熟悉文档开发知识更新库gitee.com…

C#预处理器指令(巨细版)

文章目录 一、预处理器指令的基本概念二、预处理器指令的基本规则三、C# 预处理器指令详解3.1 #define 和 #undef3.2 #if、#else、#elif 和 #endif3.3 #line3.4 #error 和 #warning3.5 #region 和 #endregion 四、高级应用&#xff1a;预处理器指令的最佳实践4.1 条件编译的最佳…

hololens 2 投屏 报错

使用Microsoft HoloLens投屏时&#xff0c;ip地址填对了&#xff0c;但是仍然报错&#xff0c;说hololens 2没有打开&#xff0c; 首先检查 开发人员选项 都打开&#xff0c;设备门户也打开 然后检查系统–体验共享&#xff0c;把共享都打开就可以了

【优选算法】双指针 -- 快乐数 和 盛最多水的容器

前言&#xff1a; &#x1f3af;个人博客&#xff1a;Dream_Chaser &#x1f388;刷题专栏&#xff1a;优选算法篇 &#x1f4da;本篇内容&#xff1a;03快乐数 和 04盛最多水的容器 目录 一、快乐数&#xff08;medium&#xff09; 1. 题⽬链接&#xff1a;202. 快乐数 2. …

详解TCP的三次握手和四次挥手

文章目录 1. TCP报文的头部结构2. 三次握手的原理与过程三次握手连接建立过程解析 3. 四次挥手的原理与过程四次挥手连接关闭过程的解析 4. 常见面试题 深入理解TCP连接&#xff1a;三次握手和四次挥手 在网络通信中&#xff0c;TCP&#xff08;传输控制协议&#xff09;扮演着…

在低成本loT mcu上实现深度神经网络端到端自动部署-深度神经网络、物联网、边缘计算、DNN加速——文末完整资料

目录 前言 DNN 量化神经网络 并行超低功耗计算范式 面向内存的部署 结果 原文与源码下载链接 REFERENCES 前言 在物联网极端边缘的终端节点上部署深度神经网络( Deep Neural Networks&#xff0c;DNNs )是支持普适深度学习增强应用的关键手段。基于低成本MCU的终端节点…