多线程之Callable接口、ReentrantLock、信号量 Semaphore以及CountDownLatch

news/2024/11/15 2:29:19/

目录:

一、Callable接口

Callable的用法
小结

二、ReentrantLock

ReentrantLock 的用法
ReentrantLock 和 synchronized 的区别?
为什么有了 synchronized 还需要 juc(java.util.concurrent) 下的 lock?

三、信号量 Semaphore

如何理解信号量?
信号量可以用在过哪些场景下?
线程同步的方式有哪些?

四、CountDownLatch


一、Callable接口

Callable的用法

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

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

  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 <= 1000; i++) {sum += i;}return sum;}};FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread t = new Thread(futureTask);t.start();int ret = futureTask.get();System.out.println(ret);}

1.创建一个匿名内部类, 实现 Callable 接口,Callable 带有泛型参数,泛型参数表示返回值的类型。

2.重写 Callable 的 call 方法, 完成累加的过程,直接通过返回值返回计算结果。

3.把 callable 实例使用 FutureTask 包装一下。

4.创建线程, 线程的构造方法传入 FutureTask ,此时新线程就会执行 FutureTask 内部的 Callable 的

call 方法, 完成计算,计算结果就放到了 FutureTask 对象中。

5.在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕,并获取到 FutureTask 中的结

果。

小结

Callable 和 Runnable 都是描述一个 "任务",Callable 描述的是带有返回值的任务,Runnable 描述的是不带返回值的任务。

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

二、ReentrantLock

ReentrantLock 的用法

ReentrantLock可重入的,和 synchronized类似, 保证线程安全。

ReentrantLock 的用法:

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

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

unlock(): 解锁

伪代码:

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {// 业务逻辑
} finally {lock.unlock()
}
ReentrantLock 和 synchronized 的区别?(为什么有了 synchronized 还需要 juc(java.util.concurrent) 下的 lock?)

1.synchronized 是一个关键字, 是 JVM 内部实现的(可能是基于 C++ 实现的),ReentrantLock 是标准库的一个类, 在 JVM 外实现的(基于 Java 实现)。

2.synchronized 使用时不需要手动释放锁,ReentrantLock 使用时需要手动释放,ReentrantLock使用起来更灵活,但是也容易遗漏 unlock。

3.synchronized 在申请锁失败时, 会死等,ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃。

4.synchronized 是非公平锁, ReentrantLock 默认是非公平锁,但是可以通过构造方法传入一个 true 开启公平锁模式。

5.ReentrantLock 有更强大的唤醒机制。synchronized 是通过 Object 的 wait / notify 实现等待-唤醒,每次唤醒的是一

个随机等待的线程。而ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程。

如何选择使用哪个锁?

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

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

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

三、信号量 Semaphore

如何理解信号量?

信号量用来表示 "可用资源的个数",其本质上就是一个计数器。Semaphore 的 PV 操作中的加减计数器操作都是原子的, 可以在多线程环境下直接使用。

代码示例:

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

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

3.创建 20 个线程, 每个线程都尝试申请资源, sleep 1秒之后, 释放资源

观察程序的执行效果:当申请到4个资源后,就会进行线程等待,等待资源的释放。

public static void main(String[] args) {Semaphore semaphore = new Semaphore(4);Runnable runnable = new Runnable() {@Overridepublic void run() {try {System.out.println("申请资源");semaphore.acquire();System.out.println("我获取到资源了");Thread.sleep(1000);System.out.println("我释放资源了");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}};for (int i = 0; i < 20; i++) {Thread t = new Thread(runnable);t.start();}}
信号量可以用在过哪些场景下?

信号量是用来表示 "可用资源的个数",其本质上就是一个计数器。使用信号量可以实现 "共享锁", 比如某个资源允许 3 个线程同时使用, 那么就可以使用 P 操作(acquire 方法)作为加锁, V 操作(release 方法)作为解锁, 前三个线程的 P 操作都能顺利返回, 后续线程再进行 P 操作就会阻塞等待,直到前面的线程执行了 V 操作。

线程同步的方式有哪些?

synchronized, ReentrantLock, Semaphore 等都可以用于线程同步。

四、CountDownLatch

CountDownLatch是同时等待 N 个任务执行结束。

代码示例:

1.构造 CountDownLatch 实例, 初始化 10 表示有 10 个任务需要完成

2.每个任务执行完毕, 都调用 latch.countDown(),在 CountDownLatch 内部的计数器同时自减

3.主线程中使用 latch.await(), 阻塞等待所有任务执行完毕,相当于计数器为 0 了

 public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(10);Runnable runnable = new Runnable() {@Overridepublic void run() {try {Thread.sleep((long) (Math.random()*10000));latch.countDown();} catch (InterruptedException e) {e.printStackTrace();}}};for (int i = 0; i < 10; i++) {new Thread(runnable).start();}latch.await();System.out.println("10个任务全部结束");}


http://www.ppmy.cn/news/21102.html

相关文章

gma 地理空间绘图:(1)绘制简单的世界地图-1.地图绘制与细节调整

了解 gma gma 是什么&#xff1f; gma 是一个基于 Python 的地理、气象数据快速处理和数据分析函数包&#xff08;Geographic and Meteorological Analysis&#xff0c;gma&#xff09;。gma 网站&#xff1a;地理与气象分析库。 gma 的主要功能有哪些&#xff1f; 气候气象&a…

操作系统(四)--进程的地址空间

目录 一、引言 二、进程的地址空间 ------> 2.1、进程的段 ------> 2.2、查看地址空间 ------------> 2.2.1、静态链接 ------------> 2.2.2、动态链接 ------------> 2.2.3、无名段 ------------> 2.2.4、vdso、vvaar、vsyscall 三、mmap ------&…

第八届蓝桥杯省赛 C++ A/B组 - 分巧克力

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;后缀表达式 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥杯的小伙伴整理常考算法题解&#xff0c;祝大…

ElasticSearch从入门到出门【上】

文章目录初识elasticsearch了解ESelasticsearch的作用ELK技术栈elasticsearch和lucene为什么不是其他搜索技术&#xff1f;倒排索引正向索引倒排索引正向和倒排ES的一些概念文档和字段索引和映射mysql与elasticsearch安装elasticsearch部署单点es部署kibana安装IK分词器在线安装…

VS搭载Sqlite3用法详解

本篇采用动态库静态调用方式&#xff0c;动态库链接如下https://download.csdn.net/download/u014272404/87406250静态调用方式&#xff1a;1.将Database.dll sqlite3.dll 添加到执行目录中2.在stdafx.h中包含&#xff08;工程目录下&#xff09;#include "Include/DataBa…

Kubernetes考试题(CKA)

CKA题目 每次还原初始化快照后&#xff0c;开机后等5分钟&#xff0c;ssh登录node01&#xff08;11.0.1.112&#xff09;检查一下所有的pod&#xff0c;确保都为Running&#xff0c;再开始练习。 kubectl get pod -A1、权限控制RBAC 问题 设置配置环境&#xff1a; [candi…

Windows10安装java环境

Windows10安装java环境 文章目录Windows10安装java环境下载解压配置下载 Java8 https://www.oracle.com/java/technologies/downloads/#java8-windows Java11 https://www.oracle.com/java/technologies/downloads/#java11-windows Java17 https://www.oracle.com/java/techno…

tomcat多实例优化及zabbix监控群集

tomcat简介Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目&#xff0c;由Apache,Sun和其他一些公司及个人共同开发而成。Tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和…