当前位置: 首页 > news >正文

【JavaEE】JUC 常见的类 -- 多线程篇(8)

JUC 常见的类

  • 1. Callable 接口
  • 2. ReentrantLock
  • 3. 原子类
  • 4. 线程池
  • 5. 信号量 Semaphore
  • 6. CountDownLatch

1. Callable 接口

  • Callable Interface 也是一种创建线程的方式
    • Runnable 能表示一个任务 (run方法) – 返回 void
    • Callable 也能表示一个任务(call方法) 返回一个具体的的值, 类型可以通过泛型参数来指定(Object)

代码示例: 创建线程计算 1 + 2 + 3 + … + 1000, 不使用 Callable 版本

  • 创建一个类 Result , 包含一个 sum 表示最终结果, lock 表示线程同步使用的锁对象.
  • main 方法中先创建 Result 实例, 然后创建一个线程 t. 在线程内部计算 1 + 2 + 3 + … + 1000.
  • 主线程同时使用 wait 等待线程 t 计算结束. (注意, 如果执行到 wait 之前, 线程 t 已经计算完了, 就不必等待了).
  • 当线程 t 计算完毕后, 通过 notify 唤醒主线程, 主线程再打印结果.
static class Result {public int sum = 0;public Object lock = new Object();
}
public static void main(String[] args) throws InterruptedException {Result result = new Result();Thread t = new Thread() {@Overridepublic void run() {int sum = 0;for (int i = 1; i <= 1000; i++) {sum += i;}synchronized (result.lock) {result.sum = sum;result.lock.notify();}}};t.start();synchronized (result.lock) {while (result.sum == 0) {result.lock.wait();}System.out.println(result.sum);}
}

可以看到, 上述代码需要一个辅助类 Result, 还需要使用一系列的加锁和 wait notify 操作, 代码复杂, 容易出错.


代码示例: 创建线程计算 1 + 2 + 3 + … + 1000, 使用 Callable 版本

  • 创建一个匿名内部类, 实现 Callable 接口. Callable 带有泛型参数. 泛型参数表示返回值的类型.
  • 重写 Callable 的 call 方法, 完成累加的过程. 直接通过返回值返回计算结果.
  • 把 callable 实例使用 FutureTask 包装一下.
  • 创建线程, 线程的构造方法传入 FutureTask . 此时新线程就会执行 FutureTask 内部的 Callable 的call 方法, 完成计算. 计算结果就放到了 FutureTask 对象中.
  • 在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的结果.
// 使用 Callable 版本实现 1 累加到 100
public static void main(String[] args) throws ExecutionException, InterruptedException {// 1. 创建实现Callable接口的匿名内部类Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 1; i <= 100; i++) {sum += i;}return sum;}};// 2. 创建futureTask类 -- 要来存储callable的返回值FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread t = new Thread(futureTask);t.start();// 3. 调用futureTask的get方法 -- 该方法是阻塞等待的Integer result = futureTask.get();System.out.println(result);
}
  • 可以看到, 使用 Callable 和 FutureTask 之后, 代码简化了很多, 也不必手动写线程同步代码了.

理解Callable

  • Callable 和 Runnable 相对, 都是描述一个 “任务”. Callable 描述的是带有返回值的任务, Runnable 描述的是不带返回值的任务.
  • Callable 通常需要搭配 FutureTask 来使用. FutureTask 用来保存 Callable 的返回结果. 因为Callable 往往是在另一个线程中执行的, 啥时候执行完并不确定.
  • FutureTask 就可以负责这个等待结果出来的工作.

2. ReentrantLock

可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全.

ReentrantLock 的用法:

  • lock(): 加锁, 如果获取不到锁就死等.
  • trylock(超时时间): 加锁, 如果获取不到锁, 等待一定的时间之后就放弃加锁.
  • unlock(): 解锁

在这里插入图片描述

ReentrantLock 和 synchronized 的区别:

  • synchronized 是一个关键字, 是 JVM 内部实现的(大概率是基于 C++ 实现). ReentrantLock 是标准库的一个类, 在 JVM 外实现的(基于 Java 实现).
  • synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活, 但是也容易遗漏 unlock.
  • synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃.
  • synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式.在这里插入图片描述
  • 更强大的唤醒机制. synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是一个随机等待的线程. ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.

如何选择使用哪个锁?

  • 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.
  • 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.
  • 如果需要使用公平锁, 使用 ReentrantLock.

3. 原子类

使用示例

在这里插入图片描述

原子类的应用场景

  1. 计数需求
    • 播放量, 点赞量…
    • 同一个视频, 有很多人都在同时的播放/点赞
  2. 统计效果
    • 统计出现错误的请求数目 – 使用原子类, 记录出错的请求数目 – 另外写一个监控服务器, 获取线上服务器的这些错误技术, 并且以曲线图的方式绘制到页面上 – 某次发布程序之后, 发现突然这里的错误数大幅度上升, 说明你这个版本代码大概率存在 bug
    • 统计收到请求的总数 (衡量服务器的压力)
    • 统计每个请求的响应事件 -> 平均的响应事件 (衡量服务器的运行效率)
    • 线上服务通过这些统计内容, 进行简单技术 -> 实现监控服务器

4. 线程池

  • 之前的文章里已经讲过了, 请跳转: 线程池

在这里插入图片描述

5. 信号量 Semaphore

信号量的概念

  • 信号量, 用来表示 “可用资源的个数”. 本质上就是一个计数器.

在这里插入图片描述

Semaphore 使用示例

public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(4);int count = 0;// acquire方法 -- P操作 -- 计数器减一semaphore.acquire();System.out.println(count++);// release方法 -- V操作 -- 计数器加一semaphore.release();semaphore.acquire();System.out.println(count++);semaphore.acquire();System.out.println(count++);semaphore.acquire();System.out.println(count++);semaphore.acquire();System.out.println(count++);semaphore.acquire();System.out.println(count++);semaphore.acquire();System.out.println(count++);
}

运行结果如下;
在这里插入图片描述

6. CountDownLatch

  • 同时等待 N 个任务执行结束.

好像跑步比赛,10个选手依次就位,哨声响才同时出发;所有选手都通过终点,才能公布成绩。

应用场景举例

  • 下载一个大文件, 将大文件分成几个小的文件, 分别让多个线程来执行相应的下载任务, 当所有线程完成任务的时候, 该大文件也就下载完成了;
  • CountDownLatch 就是用来等待所有线程完成任务的
  • 和 join不同的是, join表示执行任务的线程退出了; CountDownLatch 只是等线程完成任务, 线程只要告知CountDownLatch 我完成任务即可, 可以不用被销毁

使用举例

public static void main(String[] args) throws InterruptedException {// 构造方法中, 指定创建几个任务.CountDownLatch countDownLatch = new CountDownLatch(10);for (int i = 0; i < 10; i++) {int id = i + 1;Thread t = new Thread(() -> {System.out.println("线程" + id + "正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("线程" + id + "完成工作");// 每个任务执行结束这里, 调用一下方法// 把 10 个线程想象成短跑比赛的 10 个运动员. countDown 就是运动员撞线了.countDownLatch.countDown();// 假设线程不退出while (true);});t.start();}// 主线程如何知道上述所有的任务都完成了呢??// 难道要在主线程中调用 10 次 join 嘛?// 万一要是任务结束, 但是线程不需要结束, join 不就也不行了嘛?// 主线程中可以使用 countDownLatch 负责等待任务结束.// a => all 等待所有任务结束. 当调用 countDown 次数 < 初始设置的次数, await 就会阻塞.countDownLatch.await();System.out.println("多个线程的所有任务都执行完毕了");}

相关文章:

【JavaEE】JUC 常见的类 -- 多线程篇(8)

JUC 常见的类 1. Callable 接口2. ReentrantLock3. 原子类4. 线程池5. 信号量 Semaphore6. CountDownLatch 1. Callable 接口 Callable Interface 也是一种创建线程的方式 Runnable 能表示一个任务 (run方法) – 返回 voidCallable 也能表示一个任务(call方法) 返回一个具体的…...

java项目运行时信息获取

大体思路如下&#xff0c;想要获取启动时处理器数量、jvm 相关信息&#xff0c;操作系统信息、运行机器信息 运行机器信息 import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.lang.invoke.MethodHandles;/*** 机器工具类*/ public abstract class ServerU…...

【LeetCode】71. 简化路径

1 问题 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 / 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表示当前目录本身&#xf…...

操作系统【OS】进程的控制【进程的创建、终止、阻塞、唤醒】

定义和过程 对应事件 创建 允许一个进程创建另一个进程允许子进程继承父进程所拥有的资源创建进程的过程如下&#xff1a; 申请一个空白的 PCB&#xff0c;并向 PCB 中填写一些控制和管理进程的信息&#xff0c;比如进程的唯一标识等&#xff1b;为该进程分配运行时所必需的…...

写一个简单的解释器(2) 构建标记流

确定标记类型 分为几个大类&#xff1a; 用户符号&#xff08;类型/标识符/数字/字符串…)关键字 (流程控制和定义符)括号 &#xff08;这里暂时认为 [] 属于括号&#xff09;分号 上述四类标记基本囊括了 vc \texttt{vc} vc 中的所有最小单元的类型&#xff0c;但是因为构…...

Leetcode1833. 雪糕的最大数量

Every day a Leetcode 题目来源&#xff1a;1833. 雪糕的最大数量 解法1&#xff1a;贪心 排序 本题唯一的难点在于计数排序。 计数排序详解&#xff1a;C算法之计数排序 为了尽可能多的买到雪糕&#xff0c;我们选择从价格低的雪糕开始买&#xff0c;统计能够买到的雪糕…...

idea 里 没有svn选项的处理办法

总结一下没有svn选项的几种情况&#xff1a; 情况1&#xff1a;IntelliJ IDEA打开带SVN信息的项目不显示SVN信息&#xff0c;项目右键SVN以及图标还有Changes都不显示解决方法 在VCS菜单中有个开关&#xff0c;叫Enabled Version Control Integration&#xff0c;在打开的窗口…...

基于SpringBoot的招生管理系统

基于SpringBoot的招生管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 登录界面 管理员界面 用户界面 摘要 基于SpringBoot的招生管理系统是一款现…...

01、MySQL-------性能优化

目录 一、影响性能的相关因素存储过程&#xff1a; 二、sql优化1>、Mysql系统架构2>、引擎区别&#xff1a; 3>、索引1、什么是索引&#xff1f;联合主键索引理解&#xff1a;索引长度理解&#xff1a;什么是慢查询&#xff1f; 1&#xff09;、索引理解2&#xff09;…...

Flutter - APP跳转高德、百度、腾讯、谷歌地图

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 这里介绍的是不需要自己开发地图&#xff0c;直接通过给定的经纬度&#xff0c;跳转到三方地图APP调用导航的方式 一种是写的工具类&#xff0c;一种是通过调用三方…...

Flyway Desktop updated

Flyway Desktop updated 为比较工件序列化和反序列化添加了额外的调试日志记录。 Flyway Desktop现在将记住以前用于创建项目和匹配克隆的位置。 新的脱机许可工作流现在已在Microsoft Windows上启用。 现在&#xff0c;在配置目标数据库列表时&#xff0c;环境ID是可见的。 现…...

阿里云短信服务设置操作项目

在这里插入图片描述...

学习笔记|串口通信实战|简易串口控制器|sprintf函数|STC32G单片机视频开发教程(冲哥)|第二十一集(下):串口与PC通信

目录 3.串口通信实战实操简易的工作原理Tips:sprintf函数简介 总结课后练习 3.串口通信实战 做一个简易串口控制器。发送对应指令&#xff0c;让板子做相应的事情&#xff0c;或者传输数据&#xff08;文本模式下发送&#xff0c;不要选择HEX&#xff09;。 1.串口发送字符Ax\…...

卷积神经网络CNN学习笔记-卷积计算Conv2D函数的理解

目录 1.全连接层存在的问题2.卷积运算3.填充(padding)3.1填充(padding)的意义 4.步幅(stride)5.三维数据的卷积运算6.结合方块思考7.批处理8.Conv2D函数解析9.conv2d代码9.1 stride19.2 stride2 参考文章 1.全连接层存在的问题 在全连接层中&#xff0c;相邻层的神经元全部连接…...

收藏,安装报错信息汇总,MacOS上安装Adobe等软件/插件报错问题解决合集

打开允许“允许任何来源” 如何打开允许任何来源&#xff1f;在 Finder 菜单栏选择 【前往】 – 【实用工具 】&#xff0c;找到【终端】程序&#xff0c;双击打开&#xff0c;在终端窗口中输入&#xff1a;sudo spctl --master-disable 输入代码后&#xff0c;按【return 回车…...

Qt 报错:munmap_chunk(): invalid pointer解决方案

问题 在用Qt写程序的时候。报了munmap_chunk(): invalid pointer这个错误消息。 造成原因 在 Qt 程序中&#xff0c;这种错误可能出现在多种情况下&#xff0c;以下几点是容易造成此问题的原因&#xff1a; 函数未实现返回值&#xff1a;函数有返回值&#xff0c;但函数体中…...

【Java题】实现继承和多态的例子

一&#xff1a;题目 1.员工类Employee&#xff1a; &#xff08;1&#xff09;私有成员变量&#xff1a;姓名&#xff0c;年龄&#xff0c;工资 &#xff08;2&#xff09;提供无参&#xff0c;有参构造 &#xff08;3&#xff09;成员方法&#xff1a;work()方法——员工工作 …...

‘conda‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

原因&#xff1a;环境变量没有正确添加解决&#xff1a;我的电脑—右键属性—高级系统设置—环境变量—系统变量—Path—双击进入—新建&#xff0c;去安装Anaconda的目录下&#xff0c;找到Library中的bin&#xff0c;将此时的路径粘贴到此处...

C1N短网址 - 是如何做到行业领先的

今天从技术角度来聊下短网址的一些事情&#xff0c;市面上的短网址发展基本上经历了几个阶段。 短网址发展的几个阶段&#xff1a; 第一阶段&#xff1a;网址缩短&#xff0c;很纯粹的功能&#xff0c;各个大小公司都在做&#xff0c;门槛很低。典型代表&#xff1a;百度短网…...

【UE5】引入C++插件Plugins不在UE里出现

原因 未编译过C 原项目为蓝图项目&#xff0c;或者虽然为C项目&#xff0c;但并为编译过C. 解决 创建一个C脚本&#xff0c;让编辑器重启重新编译一遍。 如还不行&#xff0c;则打开Plugins插件面板&#xff0c;创建一个空的新的插件&#xff0c;再让引擎自动重启重新编译…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...