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

【JavaEE初阶】JUC(java.uitl.concurrent)的常见类

🌴Callable 接口

之前讲到Runnable接口,用于描述一个任务,通过Runnable中的run方法来体现的,但是描述的任务没有返回值(run方法是返回void),此处的Callable接口与Runnable接口类似,Callable里面的call方法是有返回值,表示这个线程执行结束要得到的结果是啥

 比如我们有以下需求

创建线程计算 1 + 2 + 3 + … + 1000, 如果我们不使用Runnable

使用 Runnable 的实现过程如下:

代码:

这个代码就相当于是让sum在t线程也使用,主线程也使用,这样使主线程和t1线程耦合太大了,若线程较多,就会更加麻烦。

使用Callable就是为了更优雅的解决上述问题

使用 Callable的实现过程如下:

  • 创建一个匿名内部类, 实现 Callable 接口. Callable 带有泛型参数. 泛型参数表示返回值的类型.
  • 重写 Callable 的 call 方法, 完成累加的过程. 直接通过返回值返回计算结果.
  • 把 callable 实例使用 FutureTask 包装一下.
  • 创建线程, 线程的构造方法传入 FutureTask . 此时新线程就会执行 FutureTask 内部的 Callable 的call 方法, 完成计算. 计算结果就放到了 FutureTask 对象中.
  • 在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的结果

注意:不是直接将 Callable 引言直接传给Thread,Thread并没有这种构造方法来传入Callable。

标准库提供了一个"辅助"的类,借助这个"辅助"的类对Callable进行包装一层(FutureTask)

总结:

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

举个例子可以更好的理解理解 FutureTask:

我们在吃麻辣烫的时候,选完菜之后,服务员都会给我一张小票(取餐号),这个小票就相当于使FutureTask,后面我们可以随时凭这张小票去查看自己的这份麻辣烫做出来了没

🚩 相关面试题:

🎄ReentrantLock

首先怎么读:Re 鹌鹑 t  唠嗑

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

我们从字面意思上就可以知道 “Reentrant” 这个单词的原意就是 "可重入”,

所以ReentrantLock 也是可重入锁。

🚩ReentrantLock 的用法

  • lock(): 加锁, 如果获取不到锁就死等.

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

  • unlock(): 解锁

我们在使用ReentrantLock 的时候,我们可能会忘记解锁(如在加锁之后判定条件里面有return,或者抛异常......),若确实要使用ReentrantLock,搭配finally来使用,将解锁操作放到finally中

代码:

🚩ReentrantLock 和 synchronized 的区别

  • synchronized 只是非公平锁,ReentrantLock 提供了公平锁的实现(默认是非公平锁,需要在构造这个对象的时候,指定一个参数true,才为公平锁)

  • ReentrantLock提供tryLock操作,给加锁提供了更多的可操作空间,尝试加锁,如果锁已经被获取到了,直接返回失败,而不会继续等待

  • 更强大的唤醒机制.
    synchronized 是通过 Object 的 wait / notify 实现等待-唤醒.每次唤醒的是一个随机等待的线程.
    ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.
  • synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活,但是也容易遗漏 unlock.

🚩如何选择使用哪个锁?

在实际应用中我们该如何选择锁呢?

  • 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.

  • 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.

  • 如果需要使用公平锁, 使用 ReentrantLock

大部分场景都是用 synchronized。

🎍原子类

之前讲过,原子类内部用的是 CAS 实现,所以性能要比加锁实现 i++ 高很多。原子类有以下几个

  • AtomicBoolean

  • AtomicInteger

  • AtomicIntegerArray

  • AtomicLong

  • AtomicReference

  • AtomicStampedReference

这里以 AtomicInteger 举例,常见方法有:

🎋线程池

线程池在前面也讲述过,这里不在讨论~~

🌳信号量 Semaphore

读法:se mer fo

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

听起来高大上,其实不难理解:

可以把信号量想象成是停车场的展示牌: 当前有车位 100 个. 表示有 100 个可用资源.
当有车开进去的时候, 就相当于申请一个可用资源, 可用车位就 -1 (这个称为信号量的 P 操作)
当有车开出来的时候, 就相当于释放一个可用资源, 可用车位就 +1 (这个称为信号量的 V 操作)
如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源

围绕信号量有两个基本操作:

  • P 操作 计数器 -1  申请资源
  • V 操作 计数器 +1 释放资源

代码:

  • 创建 Semaphore 示例, 初始化为 4, 表示有 4 个可用资源.

  • acquire 方法表示申请资源(P操作), release 方法表示释放资源(V操作)

注意:        

使用信号量解决线程安全问题:

🍀 CountDownLatch

相对来说比较实用的工具类,当我们把一个任务拆分成多个的时候,就可以通过这个工具类来识别任务是否整体执行完毕。

CountDownLatch的作用为:同时等待 N 个任务执行结束

把一个大文件拆分成20个部分,每个线程都独立和人家服务器建立连接,分20个连接进行下载,等所有线程下载完毕之后,再将结果进行合并,就需要通过CountDownLatch来识别出所有线程都执行完毕。

接下来我们依旧用代码实例进行讲解:

  • 构造 CountDownLatch 实例, 初始化 10 表示有 10 个任务需要完成.
  • 每个任务执行完毕, 都调用 latch.countDown() . 在 CountDownLatch 内部的计数器同时递减.
  • 主线程中使用 latch.await(); 阻塞等待所有任务执行完毕. 相当于计数器为 0

代码:

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java Server-Sent Event 服务端发送事件
  • [FSCTF 2023]细狗2.0
  • 力扣热题100_二叉树_94_二叉树的中序遍历
  • C语言中常用的函数
  • python自动化笔记:excel文件处理及日志收集
  • 列式数据库(HBase) 中实现表与表的关联
  • 区块链(Blockchain)
  • 【代码随想录】长度最小的子数组——滑动窗口
  • 第二十一节、敌人追击状态的转换
  • 【K8S】K8S架构及相关组件
  • PHP反序列化POP链构造:理解与利用
  • IT运维岗适用的6本证书
  • 如何在前后端分离项目中,使用Spring Security
  • 英特尔凌动® P5300 和 P5700 处理器使企业能够优化现代网络基础架构、安全加速器和存储设备之间的性能和成本平衡。
  • leetcode:1822. 数组元素积的符号(python3解法)
  • 2017 前端面试准备 - 收藏集 - 掘金
  • android 一些 utils
  • Android优雅地处理按钮重复点击
  • CSS 三角实现
  • flask接收请求并推入栈
  • hadoop集群管理系统搭建规划说明
  • HTTP请求重发
  • Idea+maven+scala构建包并在spark on yarn 运行
  • js学习笔记
  • Markdown 语法简单说明
  • mysql innodb 索引使用指南
  • Nodejs和JavaWeb协助开发
  • python学习笔记 - ThreadLocal
  • Ruby 2.x 源代码分析:扩展 概述
  • spring security oauth2 password授权模式
  • 前端学习笔记之观察者模式
  • 使用SAX解析XML
  • 提醒我喝水chrome插件开发指南
  • 推荐一个React的管理后台框架
  • 想写好前端,先练好内功
  • - 转 Ext2.0 form使用实例
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • Semaphore
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • ## 基础知识
  • #java学习笔记(面向对象)----(未完结)
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • (10)ATF MMU转换表
  • (6)设计一个TimeMap
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (附源码)ssm码农论坛 毕业设计 231126
  • (七)c52学习之旅-中断
  • (实战篇)如何缓存数据
  • (学习日记)2024.02.29:UCOSIII第二节
  • ***利用Ms05002溢出找“肉鸡
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .net core docker部署教程和细节问题
  • .net core 的缓存方案