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

多线程进阶——JUC的常见类

目录

一、Callable接口

(一)Callable带有泛型参数

(二)Callable中有带返回值的call()方法

(三)Callable实例要用FutureTask包装一下,再在线程构造方法中传入FutureTask。

(四)代码示例

二、ReentrantLock 

 synchronized和ReentrantLock之间的区别

三、信号量Semaphore 

四、CountDownLatch


一、Callable接口

Callable是一个interfa,相当于把线程封装了一个“返回值”,方便程序员借助多线程的方式计算结果。

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

(一)Callable带有泛型参数

Callable带有泛型参数,泛型参数表示返回值的类型。

Callable<Integer> callable = new Callable<Integer>(){}

(二)Callable中有带返回值的call()方法

call()方法类似于Runnable中的void run()方法,有返回值。

public Integer call() throws Exception {}

(三)Callable实例要用FutureTask包装一下,再在线程构造方法中传入FutureTask。

想象去吃麻辣烫。当餐点好后,后厨就开始做了。同时前台会给你一张 "小票"。这个小票就是 FutureTask。后面我们可以随时凭这张小票去查看自己的这份麻辣烫做出来了没。 

FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread t = new Thread(futureTask);

(四)代码示例

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

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

代码:

public class Demo30 {public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<Integer> callable=new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum=0;for(int i=1;i<1001;i++){sum+=i;}return sum;}};FutureTask<Integer> futureTask=new FutureTask<>(callable);Thread t=new Thread(futureTask);t.start();int result=futureTask.get();System.out.println("sum:"+result);}
}

 如果不使用Callable和FutureTask,则要考虑线程同步的问题,要等执行运算的线程结束后,再执行main线程里的输出结果。

二、ReentrantLock 

可重入互斥锁
和 synchronized 定位类似,都是用来实现互斥效果,保证线程安全。
ReentrantLock 也是可重入锁。“Reentrant”这个单词的原意就是“可重入”。

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

ReentrantLock lock = new ReentrantLock();lock.lock();try {// working} finally {lock.unlock()}

 synchronized和ReentrantLock之间的区别

  1. synchronized是关键字(内部实现是JVM内部通过C++实现的),ReentrantLock是标准库的类。
  2. synchronized通过代码块控制加锁解锁,ReentrantLock需要lock/unlock方法,但是要注意不要漏写unlock。
  3. ReentrantLock除了提供lock/unlock方法,还提供了trylock方法,trylock方法不会造成阻塞,加锁成功返回true,失败返回flase,调用者判定返回值,决定接下来如何做。
  4. ReentrantLock提供了公平锁的实现。
  5. ReentrantLock搭配的等待通知机制,是Condition类,相比于wait/notify来说功能更强大一些。

三、信号量Semaphore 

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

创建初始值为1的信号量:

Semaphore semaphore = new Semaphore(1);

申请资源:

semaphore.acquire();

 释放资源:

semaphore.release();


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

 信号量的一个特殊情况:初始值为1的信号量,取值要么是1要么是0(二元信号量)

等价于“锁”(普通的信号量,就相当于锁的更广泛推广),如果是普通的N的信号量,就可以限制同时有多少个线程来执行某个逻辑。

四、CountDownLatch

使用多线程,经常把一个大的任务拆分成多个子任务,使用多线程执行这些子任务,从而提高程序的效率。

如何衡量这些子任务都完成了呢?整个任务都完成了呢?

CountDownLatch表示同时等待 N 个任务执行结束。
好像跑步比赛,10个选手依次就位,哨声响才同时出发;所有选手都通过终点,才能公布成绩。

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

 代码示例:

public class Demo31 {public static void main(String[] args) throws InterruptedException {Random random=new Random(10);CountDownLatch count=new CountDownLatch(10);Runnable runnable=new Runnable() {@Overridepublic void run() {int sum= random.nextInt();System.out.println(sum);count.countDown();}};//创建10个线程执行runnablefor(int i=0;i<10;i++){new Thread(runnable).start();}count.await();System.out.println("执行结束");}
}

http://www.lryc.cn/news/585921.html

相关文章:

  • w460实习生管理系统
  • 接口测试及常用接口测试工具总结
  • Springboot aop面向对象编程
  • JavaScript加强篇——第六章 定时器(延时函数)与JS执行机制
  • 【电脑】硬盘驱动器(HDD)的基础知识
  • TCP详解——各标志位
  • QML与C++相互调用函数并获得返回值
  • 浅谈 Pydantic v2 的 RootModel 与联合类型——构建多请求结构的统一入口模型
  • Linux中的git命令
  • Kimi K2万亿参数开源模型原理介绍
  • 猿人学js逆向比赛第一届第二十题
  • Linux进程的生命周期:状态定义、转换与特殊场景
  • 杭州乐湾科技有限公司的背景、产品体系与技术能力的全方位深度分析
  • linux_线程概念
  • 车载操作系统 --- Linux实时化与硬实时RTOS综述
  • windows电脑远程win系统服务器上的wsl2
  • 部署Harbor私有仓库
  • 服务器怎么跑Python项目?
  • vite如何生成gzip,并在服务器上如何设置开启
  • 自动化证书续签工具针对VPS服务器HTTPS服务的维护实践
  • Python技巧记录
  • 浅谈npm,cnpm,pnpm,npx,nvm,yarn之间的区别
  • 【云端深度学习训练与部署平台】AutoDL连接VSCode运行深度学习项目的全流程
  • Go语言中的Options模式
  • Mac M芯片安装RocketMQ服务
  • tp8.0\jwt接口安全验证
  • 深入解析 LinkedList
  • 在 Android 库模块(AAR)中,BuildConfig 默认不会自动生成 VERSION_CODE 和 VERSION_NAME 字段
  • 手机识别数据集,2628张原始图片,支持yolo,coco json,pasical voc xml等格式的标注
  • 搭建云手机教程