Java 多线程系列Ⅵ(并发编程的五大组件)

news2024/11/25 5:01:13

JUC 组件

  • 前言
  • 一、Callable
  • 二、ReentrantLock
  • 三、Atomic 原子类
  • 四、线程池
  • 五、Semaphore
  • 六、CountDownLatch


前言

JUC(Java.util.concurrent)是 Java 标准库中的一个包,它提供了一组并发编程工具,本篇文章就介绍几组常见的 JUC 组件:Callable、ReentranLock、Atomic原子类、线程池、Semaphore、CountDownLatch。

一、Callable

类似于 Runnable,Callable也是一个 interface,用来描述一个任务。与Runnable接口不同的是Callable接口是描述了一个具有返回值的任务。

📝例如我们创建1个线程计算1到10000的累加和,并且要求返回结果值,我们就可以使用 Callable:

public class TestCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        
        // 使用 Callable 创建一个有返回值的任务
        // Callable 带有泛型参数,泛型参数表示返回值的类型。
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 10000; i++) {
                    sum += i;
                }
                return sum;
            }
        };
        
        // 使用 FutureTask 包装一下。相当于一张任务凭据,后续可以使用凭据拿到结果。
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        
        // 创建线程, 线程的构造方法传入 FutureTask . 此时新线程就会执行 FutureTask 内部的 Callable 的
        // call 方法, 完成计算. 计算结果就放到了 FutureTask 对象中
        Thread t = new Thread(futureTask);

		// 启动线程
        t.start();
        
        // 在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕.无需使用 join
        int sum = futureTask.get();

        System.out.println(sum);
    }
}

通过上述对Callable接口的简单使用,以及阅读代码中的注释,相信你已经对Callable接口有了一定的理解,那么Callable究竟是什么,下面我们再来总结一下:

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

  2. 另外 Callable 和 Runnable 相似,都是描述一个 “任务”。Callable 描述的是带有返回值的任务,Runnable 描述的是不带返回值的任务。Callable 通常需要搭配 FutureTask 来使用,FutureTask 用来保存 Callable 的返回结果。

  3. 因为 Callable 往往是在另一个线程中执行的,什么时候执行完并不确定,通过调用 FutureTask 的 get 方法能够阻塞等待新线程计算完毕。

实现Callable接口也是创建线程的新方式,目前为止我们已学过的线程创建如下:

  1. 继承 Thread 类:继承 Thread 类并重写其中的 run() 方法,然后创建 Thread 的子类实例并调用 start() 方法即可启动一个新线程。

  2. 实现 Runnable 接口:实现 Runnable 接口并重写其中的 run() 方法,然后创建 Thread 实例时传入该 Runnable 对象并调用 start() 方法即可启动一个新线程。

  3. 实现 Callable 接口:实现 Callable 接口并重写其中的 call() 方法,然后创建 FutureTask 对象并将其作为参数传入 Thread 构造函数中,再调用 start() 方法即可启动一个新线程。

二、ReentrantLock

synchronized 和 ReentrantLock 都是用于实现多线程同步的工具,它们的目的都是为了保证多个线程对共享资源的安全访问。但是它们的实现机制和用法略有不同:

ReentrantLock 和 synchronized 的区别

  1. synchronized 关键字是JVM内部实现的。ReentrantLock 是标准库的一个类,是JVM外部实现的。

  2. synchronized 是基于 代码块 的方式来控制加锁的,不需要手动释放锁。ReentrantLock 提供了 lockunlock 独立的方法,来进行加锁解锁,使用起来更灵活,但是需要手动释放锁。

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

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

  5. synchronized 搭配 wait、notify 进行等待唤醒,如果多个线程 wati 同一个对象,notify 将随机唤醒一个。ReentrantLock 则是搭配 Condition 这个类,这个类也能起到等待-唤醒,但是功能更强大,可以更精确控制唤醒某个指定的线程。

Tips:当然在大部分情况下 synchronized 就足够了,但是 ReentrantLock 是一个重要补充!

三、Atomic 原子类

JUC中还提供了一些原子类,原子类内部用的是 CAS 实现,性能要比加锁高得多:

  1. AtomicBoolean
  2. AtomicInteger
  3. AtomicIntegerArray
  4. AtomicLong
  5. AtomicReference
  6. AtomicStampedReference

这些原子类大家了解,可以简单使用即可,这里就不做过多的展开介绍了。

四、线程池

点击这里 --> 转到多线程案例,线程池模块介绍。

五、Semaphore

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

其实信号量就相当于生活中的停车场:

停车场中有一定数量的停车位,假设这个停车场最多只能容纳50辆汽车,那么当停车场中已经有49辆汽车时,如果进来了一辆汽车,就相当于申请一个可用资源,可用车位就 -1。(-1操作称为信号量的 P 操作)

此时计数器的值已经为 0了,即现在停车场已经没有车位可用了,如果这时再来新的汽车,就需要进行等待,直到有一辆汽车离开,相当于释放一个可用资源,可用车位就+1,此时有了停车位可用,这辆汽车才能够进入停车场停车。(+1操作称为信号量的 V 操作)

:Semaphore 的 PV 操作中的加减计数器操作都是原子的,可以在多线程环境下直接使用。

使用示例:
在 JUC 的 Semaphore 中,acquire 方法表示申请资源(P操作),release 方法表示释放资源(V操作)。

public class Demo1 {
    public static void main(String[] args) {
    	// 创建 1 个初始值为 4 的信号量
        Semaphore semaphore = new Semaphore(4);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("申请资源");
                    // acquire 方法表示申请
                    semaphore.acquire();
                    System.out.println("我获取到资源了");
                    Thread.sleep(1000);
                    System.out.println("我释放资源了");
                    // release 方法表示释放
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 20; i++) {
            Thread t = new Thread(runnable);
            t.start();
        }
    }
}

Semaphore 实际开发中的场景——共享锁

使用信号量可以实现 “共享锁”,比如某个资源允许 3 个线程同时使用, 那么就可以使用 P 操作作为加锁,V 操作作为解锁,前三个线程的 P 操作都能顺利返回,后续线程再进行 P 操作就会阻塞等待,直到前面的线程执行了 V 操作。

六、CountDownLatch

CountDownLatch 线程同步工具类,可以理解为同时等待 N 个任务执行结束。

就例如跑步比赛,10个选手依次就位,哨声响才同时出发,所有选手都通过终点,才能公布成绩,即比赛结束取决于最后一个选手冲过终点。

具体实现:

(1)构造 CountDownLatch 实例 latch,初始化 10 表示有 10 个任务需要完成.
(2)每个任务执行完毕,都调用 latch.countDown() ,在 CountDownLatch 内部的计数器同时自减。
(3)主线程中使用 latch.await(); 阻塞等待所有任务执行完毕,当计数器为 0 了,阻塞就解除,继续进行后续操作。

public class Demo2 {
    public static void main(String[] args) throws Exception {
        // 创建 1 个原子类,用于多线程计数
        AtomicInteger atomicInteger = new AtomicInteger(1);
        // 创建 1 个初始值为 10 的线程同步工具类
        CountDownLatch latch = new CountDownLatch(10);
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    // 生成一个 0-9999之间的随机数,表示比赛时间
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println("第: "+atomicInteger.getAndIncrement()+"选手完成了比赛");
                    latch.countDown();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 10; i++) {
            new Thread(r).start();
        }
        // 必须等到 10 人全部回来
        latch.await();
        System.out.println("比赛结束");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/994794.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

汇川PLC学习Day2:编写检测IO端口状态程序

汇川PLC学习Day2&#xff1a;编写检测IO端口状态程序 一、 新增IO和模拟量模块 IO组态界面 模块参数设置 程序编写 想法是将DA模块的通道0接到AD模块的通道0&#xff0c;将DA模块的通道1接到AD模块的通道1&#xff0c;PLC本身发模拟量给自己PLC收模拟量转换&#xff0c;…

MySQL 8.0.25版本下载、安装及配置(Windows 10/11 64位)详细教程【超详细,保姆级教程!!!】

本文介绍关于windows 11如何安装配置MySQL 8.0.25版本的详细步骤 MySQL下载地址&#xff08;官网&#xff09; 一、下载MySQL 8.0.25 1、进入官网&#xff0c;选择版本 8.0.25 2、下载MySQL压缩包 3、下载完成后将压缩包解压至方便自己查找的位置&#xff08;切记&#xf…

Tensor数据转换为稀疏矩阵

Tensor数据转换为稀疏矩阵 一、稀疏矩阵 原文链接 常用的稀疏矩阵存储格式有COO&#xff0c;CSR/CSC&#xff0c;LIL 1.COO COO(Coordinate format )是最为简单的格式&#xff0c;以三元组的形式存储稀疏矩阵。记录矩阵中非零元素的数值和所在的行序号和列序号。形式为&am…

工商业储能CE认证电表ADW300

安科瑞 华楠 ADW300 无线计量仪表主要用于计量低压网络的三相有功电能&#xff0c;具有体积小、精度高、功能丰富等优点&#xff0c;并且可选通讯方式多&#xff0c;可支持 RS485 通讯和 Lora、NB、4G、wifi 等无线通讯方式&#xff0c;增加了外置互感器的电流采样模式&#x…

【数据结构】线性表

线性表 顺序表链式存储单链表双链表 知识目录 顺序表 概念&#xff1a;用一组地址连续的存储单元依次存储线性表的数据元素&#xff0c;这种存储结构的线性表称为顺序表。 特点&#xff1a;逻辑上相邻的数据元素&#xff0c;物理次序也是相邻的。 只要确定好了存储线性表的…

基本数据类型和包装类型 使用规范

使用规范 1 概念1.1 基本数据类型1.2 包装类型1.3 对应关系1.4 自动装箱/拆箱 2 变量类型2.1 全局变量2.1.1 常量&#xff08;Constants&#xff09;2.1.2 类变量&#xff08;Class Variables&#xff09;2.1.3 实例变量&#xff08;Instance Variables&#xff09; 2.2 局部变…

快速实现抖音上下滑动,你不知道的ViewPager2用法,信息量巨大,建议收藏点赞。老tier~

万能ViewPager2适配器–SmartViewPager2Adapter 特点功能 完全脱离xml&#xff0c;所有效果只需要通过api调用 具体功能&#xff1a;1. 两句代码实现抖音列表效果2. 无感且丝滑&#xff0c;动态从头部或者底部加载数据3. 设置上下加载监听&#xff0c;再达到预加载limit的时…

用python实现基本数据结构【02/4】

*说明 如果需要用到这些知识却没有掌握&#xff0c;则会让人感到沮丧&#xff0c;也可能导致面试被拒。无论是花几天时间“突击”&#xff0c;还是利用零碎的时间持续学习&#xff0c;在数据结构上下点功夫都是值得的。那么Python 中有哪些数据结构呢&#xff1f;列表、字典、集…

STM32初学-外部RTC时钟芯片DS3231

RTC(Real_Time Clock)即实时时钟&#xff0c;它是电子产品中不可或缺的东西。其最直接的作用就是时钟功能。细心的朋友可以发现&#xff0c;当我们的电脑或者手机没联网时&#xff0c;仍然可以正常显示日期与时钟&#xff0c;这就是RTC的功劳。 RTC的运行无需网络连接&#xff…

python创建exe文件

1、搭建环境 pip install pyinstaller 2、准备测试代码 exe_test.py import timeprint("hello") print("hello") print("hello") print("hello")time.sleep(5) 注&#xff1a;添加sleep以便在执行exe文件的时候能看到结果 3、生…

在Windows操作系统上安装PostgreSQL数据库

在Windows操作系统上安装PostgreSQL数据库 一、在Windows操作系统上安装PostgreSQL数据库 一、在Windows操作系统上安装PostgreSQL数据库 点击 PostgreSQL可跳转至PostGreSQL的官方下载地址。 &#xff08;1&#xff09; &#xff08;2&#xff09;选择安装的目录&#xff…

入门人工智能 —— 使用 Python 进行文件读写,并完成日志记录功能(4)

入门人工智能 —— 使用 Python 进行文件读写&#xff08;4&#xff09; 入门人工智能 —— 使用 Python 进行文件读写打开文件读取文件内容读取整个文件逐行读取文件内容读取所有行并存储为列表 写入文件内容关闭文件 日志记录功能核心代码&#xff1a;完整代码&#xff1a;运…

小工具——筛选图像小工具

最近在公司手动筛图片&#xff0c;需要将某些含有检测目标的图像手动筛选出来用于做新模型的测试。我最开始是两个文件夹&#xff0c;来回复制粘贴&#xff0c;后来感觉这种效率太低了&#xff0c;就随手写了一个图像筛查小工具。代码如下&#xff1a; import sys from PyQt5.…

图论-图的深度优先遍历-Java

回顾力扣144/94//145/102/589/590/429&#xff0c;熟练掌握递归和非递归写法。 图论不强调非递归。 使用邻接表 1个连通分量 Graph.java package Chapt02_DFS; import java.io.File; import java.io.IOException; import java.util.TreeSet; import java.util.Scanner;///…

66.C++多态与虚函数

目录 1.什么是多态 2.多态的分类 3.对象转型 3.1 向上转型&#xff1a; 3.2 向下转型&#xff1a; 4.虚函数 1.什么是多态 生活中的多态&#xff0c;是指的客观的事物在人脑中的主观体现。例如&#xff0c;在路上看到⼀只哈士奇&#xff0c;你可以看做是哈士奇&#xf…

Unity实战(10):如何将某个相机的画面做成贴图(RenderTexture)

目录 前言 一、创建物体、材质与相机 二、将RenderTexture赋给材质 2.1 修改rt1的一些属性 2.2 将rtMat1材质的shader改为Unlit/Texture&#xff0c;并将rt1赋给这个材质 三、效果呈现 前言 本文记录如何将某个相机的画面做成贴图&#xff0c;即游戏某些场景中小地图做法…

时序预测 | MATLAB实现ICEEMDAN-iMPA-BiLSTM时间序列预测

时序预测 | MATLAB实现ICEEMDAN-iMPA-BiLSTM时间序列预测 目录 时序预测 | MATLAB实现ICEEMDAN-iMPA-BiLSTM时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 ICEEMDAN-iMPA-BiLSTM功率/风速预测 基于改进的自适应经验模态分解改进海洋捕食者算法双向长短期记忆…

基于SSM的农产品推广应用网站

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

华为云云耀云服务器L实例评测|centos系统搭建git私服

搭建git私服 前言一、云服务器二、服务器安装git三、服务器git配置1.创建文件用于存放公钥2.设置文件权限3.配置本地公钥 四、服务器部署git仓库四、git仓库到本地总结 前言 之前一直想搭建一个属于自己的git私服&#xff0c;然后没赶上厂商的活动&#xff0c;这次正好赶上华为…

学习笔记——树上哈希

普通子树哈希 树上的很多东西都是转化成链上问题的&#xff0c;比如树上哈希 树上哈希&#xff0c;主要是用于树的同构这个东西上的 什么是树的同构&#xff1f; 如图&#xff0c;不考虑节点编号&#xff0c;三棵树是同构的 将树转化成链&#xff0c;一般有两种方式&#xf…