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

多线程进阶——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.xdnf.cn/news/15165.html

相关文章:

  • 跨系统开发代码换行符如何解决
  • docker-compose方式搭建lnmp环境——筑梦之路
  • 2025 XYD Summer Camp 7.10 筛法
  • Go语言生态成熟度分析:为何Go还无法像Java那样实现注解式框架?
  • 系统分析师-计算机系统-计算机系统概述存储系统
  • 《Java Web程序设计》实验报告二 学习使用HTML标签、表格、表单
  • 【学习笔记】Linux命令
  • 蓝牙BT UUID的含义以及使用方法案例说明
  • JavaScript加强篇——第七章 浏览器对象与存储要点
  • 《Java Web程序设计》实验报告四 Java Script前端应用和表单验证
  • OpenCL study - code02
  • docker网络与数据持久化
  • 大数据时代UI前端的智能化服务升级:基于用户情境的主动服务设计
  • Elasticsearch 的 `modules` 目录
  • 使用Matlab整车模型进行电动汽车能耗仿真测试方法
  • 【飞算JavaAI】一站式智能开发,驱动Java开发全流程革新
  • 鸿蒙的NDK开发初级入门篇
  • Apache Iceberg数据湖高级特性及性能调优
  • 如何使用postman做接口测试?
  • 《Spring 中上下文传递的那些事儿》Part 8:构建统一上下文框架设计与实现(实战篇)
  • 安全初级作业1
  • Linux中的git命令
  • 【LeetCode 热题 100】24. 两两交换链表中的节点——(解法一)迭代+哨兵
  • 设计模式 - 面向对象原则:SOLID最佳实践
  • vscode 中的 mermaid
  • 【高等数学】第三章 微分中值定理与导数的应用——第三节 泰勒公式
  • Python 【技术面试题和HR面试题】➕ 循环结构、控制语句及综合应用问答
  • C++编程基础
  • 端口到底是个什么鬼?回答我!
  • pyQt基础4(对话框)