多线程(JUC, ReentrantLock, 原子类, 线程池, 信号量 Semaphore, CountDownLatch)

JUC

Java.util.concurrent 包, 存放了并发编程相关的组件, 目的是更好的支持高并发任务 (多线程只是实现并发编程的一种具体方式 …)


ReentrantLock

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

  • synchronized 对对象加锁, 保护临界资源
  • ReentreatLock 使用 lock 方法和 unlock 方法,加锁对象是 ReentrantLock 的实例

在这里插入图片描述

核心方法

  • lock(): 加锁, 获取不到锁就死等
  • trylock(超时时间):尝试加锁, 如果获取不到锁, 等待一段时间后就放弃加锁
  • unlock(): 解锁

ReentrantLock 使用

由于 reentreatLock 需要手动释放, 因此推荐 try finally 的写法

public class ReentreatLockDemo {
    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();
//        reentrantLock.lock();
        boolean ok = reentrantLock.tryLock();
        try {
            if(ok) {
                // do
            } else {
                // undo
            }
        } finally {
            reentrantLock.unlock();
        }
    }
}

ReentrantLock 和 synchronized 比较

  1. synchronized 是关键字, 是 JVM 内部实现的
    ReentrantLock 是标准库的一个类, 在 JVM 外实现 (基于 Java 实现)
  1. synchronized 是非公平锁
    ReentrantLock 默认是非公平锁, 但是提供了公平锁版本的实现
    ReentrantLock reentrantLock = new ReentrantLock(true);
  1. ReentrantLock 提供更灵活的加锁方式:
    ReentrantLock reentrantLock = new ReentrantLock(true);
    reentrantLock.tryLock();
  1. ReentrantLock 提供更强大, 更方便的等待通知机制
    synchronized 搭配 wait() notify()使用, notify() 是随机唤醒等待队列的线程
    ReentrantLock 搭配 Condition 类. 可以唤醒指定的线程

原子类

原子类内部用的是 CAS 实现, 更高效的解决了线程安全问题
原子类提供了线程安全的自增自减等操作


原子类有以下几种 :
在这里插入图片描述


原子类的常见方法 (以 AtomicInteger 为例)

在这里插入图片描述

public class Main {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger();
        Scanner scanner = new Scanner(System.in);
        int x = scanner.nextInt();

        atomicInteger.getAndIncrement();// i++;
        atomicInteger.incrementAndGet();// ++i;
        atomicInteger.getAndDecrement();// i--;
        atomicInteger.decrementAndGet();// --i;
        atomicInteger.addAndGet(x);     // i+=x;
        atomicInteger.get();            // x
    }
}

线程池

之前写过, 挂个链接这里不再复制粘贴了 — https://editor.csdn.net/md/?articleId=136715895


信号量 Semaphore

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

Semaphore 提供了 P,V 操作
P 操作: 申请一个可用资源, 计数器 - 1
V 操作: 释放一个可用资源, 计数器 + 1

当可用资源个数为 0 时, 再进行 P 操作, 就会出现阻塞等待清空 (资源为零, 无法继续消耗了), 直到有线程让信号量大于零, 才会唤醒该阻塞的线程

锁 可可以视为计数器为 1 的信号量, 二元信号量

  • 锁是信号量的一种特殊情况
  • 信号量是锁的一般表达

总结: 信号量的表达含义范围更广


Semaphore 的简单使用
在这里插入图片描述


代码示例

public class Main {
    public static void main(String[] args) {
        // 参数是可用资源的个数(信号量的初始值)
        Semaphore semaphore = new Semaphore(4);


        for (int i=0;i<20;i++) {
            Thread t = new Thread(() -> {
                try {
                    System.out.println("申请资源");
                    semaphore.acquire();

                    System.out.println("持有资源");
                    Thread.sleep(1000);

                    System.out.println("释放资源");
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            });
            t.start();
        }
    }
}

运行结果
在这里插入图片描述

有兴趣可以仔细看看运行结果, 同一时刻最多只有 4个线程能够持有锁, 这就是信号量的存在意义


CountDownLatch

同时等待 N 个任务执行结束 (和 join() 功能类似)


在这里插入图片描述

核心API

  • await(): 阻塞等待线程, 直至任务全部完成
  • getCount(): 获取剩余未完成任务个数
  • countDown(): 未完成任务个数 -1

代码示例

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 参数代表需要等待的任务数量
        CountDownLatch countDownLatch = new CountDownLatch(5);

        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(() -> {
                System.out.println("完成一个任务");
                // countDown() 方法, 代表完成一个任务
                countDownLatch.countDown();
            });

            t.start();
            Thread.sleep(1000);
        }
        // await()方法, 用于阻塞线程
        // 直至 countDownLatch 内任务全部完成, 才会往下继续走
        countDownLatch.await();
        System.out.println("任务全部完成");
    }
}

运行结果
在这里插入图片描述

运行过代码会发现, 每间隔一秒输出一次 “完成一个任务”, 5秒之后输出 “任务全部完成”

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

面向量产!基于视觉的速度距离估计

面向量产&#xff01;基于视觉的速度距离估计 论文名称&#xff1a;Vision-based Vehicle Speed Estimation: A Survey 导读 在精确检测车速车距的方案中&#xff0c;视觉方案是非常具有挑战性的&#xff0c;但由于没有昂贵的距离传感器而大幅降低成本&#xff0c;所以潜力巨…

【现代C++】范围基于的for循环

现代C中的范围基于的for循环&#xff08;range-based for loop&#xff09;是C11引入的一项特性&#xff0c;旨在简化对容器或范围的迭代过程。这种循环语法不仅使代码更清晰易读&#xff0c;还减少了迭代时的错误。以下是范围基于的for循环的详细介绍&#xff1a; 1. 基本用法…

Vue3的与2的简单区别

Vue2选项式api Vue3组合式API setup方法的使用&#xff0c;最后需要return setup语法糖省略了内部的export default{} 和return 内容 以及组件的注册 reactive生成响应式对象&#xff0c;只能适用于复杂对象&#xff0c;简单类型不可 ref生成响应式数据&#xff1a;复杂类型和简…

leetcode 数组练习,美团优选面试题java

public int maxSubArray(int[] nums) { int countnums[0]; int resnums[0]; for(int i1;i<nums.length;i){ if(count<0){ countnums[i]; }else{ countnums[i]; } resMath.max(res,count); } return res; } 3、两数之和 利用map,来存储数组值和当前位置&…

【Review】电动汽车百人会

汽车强国靠四化--电动化、智能化、低碳化、全球化。 1.坚持电动化&#xff1a;电动化是经过二十多年反复论证的既定战略和技术路线、不能动摇、无需改变、要将电动化进行到底&#xff0c;全力攻克下一代电动化核心技术--全固态锂电池;市场方面要采用“双轮”驱动战略一方面继续…

基于PID控制器的四旋翼无人机控制系统的simulink建模与仿真,并输出虚拟现实动画

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1四旋翼无人机的动力学模型 4.2 PID控制器设计 4.3 姿态控制实现 4.4 VR虚拟现实动画展示 5.完整工程文件 1.课题概述 基于PID控制器的四旋翼无人机控制系统的simulink建模与仿真,并输出vr虚拟现实…

政安晨:【深度学习实践】【使用 TensorFlow 和 Keras 为结构化数据构建和训练神经网络】(四)—— 过拟合和欠拟合

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 政安晨的机器学习笔记 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 通过增加容量或提前停止来提高性能。 在深度学习中&#…

Springboot 整合 Knife4j (API文档生成工具)

目录 一、Knife4j 介绍 二、Springboot 整合 Knife4j 1、pom.xml中引入依赖包 2、在application.yml 中添加 Knife4j 相关配置 3、打开 Knife4j UI界面 三、关于Knife4j框架中常用的注解 1、Api 2、ApiOperation ​3、ApiOperationSupport(order X) ​4、ApiImplici…

C# WPF编程-布局

C# WPF编程-布局 布局WPF布局原则布局过程布局容器布局属性Border控件StackPanel布局WrapPanel布局DockPanel布局Grid布局UniformGrid布局Canvas布局 布局 WPF布局原则 WPF窗口只能包含单个元素。为在WPF窗口中放置多个元素并创建更贴近实用的用户界面&#xff0c;需要在窗口…

linux系统----------MySQL索引浅探索

目录 一、数据库索引介绍 二、索引的作用 索引的副作用 (缺点) 三、创建索引的原则依据 四、索引的分类和创建 4.1普通索引 4.1.1直接创建索引 4.1.2修改表方式创建 4.1.3创建表的时候指定索引 4.2唯一索引 4.2.1直接创建唯一索引 4.2.2修改表方式创建 4.2.3创建表…

根据log信息解读内核(linux-2.6.32.24)的启动流程

目录 概述 1 从bootloader 到内核部分 2 初始化cache和CPU时钟 3 获取cache和memory信息 4 初始化cache、电源管理和中断 5 初始化USB和I2C 6 网络协议初始化 7 挂载JFFS2文件系统和初始化IO 8 初始化外围device 9 Nand Flash资源分配 10 初始化网络接口 11 注册US…

一文快速掌握docker的理念和基本使用

写在文章开头 写于一个周末&#xff0c;在复盘梳理文章时候发现这一篇关于早期了解docker时记录的文档&#xff0c;仔细阅读了一下&#xff0c;为了保证文章更加清晰以便读者使用。故再次重新一次梳理一次&#xff0c;通过这篇文章&#xff0c;你将会对docker的基本理念和基础…

Expert Prompting-引导LLM成为杰出专家

ExpertPrompting: Instructing Large Language Models to be Distinguished Experts 如果适当设计提示&#xff0c;对齐的大型语言模型&#xff08;LLM&#xff09;的回答质量可以显著提高。在本文中&#xff0c;我们提出了ExpertPrompting&#xff0c;以激发LLM作为杰出专家回…

【C语言基础】:字符串函数(二)

文章目录 一、strncpy函数的使用二、strncat函数的使用三、strncmp函数的使用四、strstr函数的使用和模拟实现4.1 strstr函数的使用4.2 strstr函数的模拟实现 五、strtok函数的使用六、strerror函数的使用 上节回顾&#xff1a;【C语言基础】&#xff1a;字符函数和字符串函数 …

【ubuntu20.04+tensorflow-gpu1.14配置】

ubuntu20.04tensorflow-gpu1.14配置 目录0. 版本注意事项说明1. 个人目录下载后配置系统环境变量2. anaconda配置所有环境&#xff08;过程简便&#xff0c;但容易出现不兼容问题&#xff09;3. 验证tensorflow-gpu4. 一些细节 目录 总结出两种方法 个人目录 下载cuda和cudnn…

分库分表场景下多维查询解决方案(用户+商户)

在采用分库分表设计时&#xff0c;通过一个PartitionKey根据散列策略将数据分散到不同的库表中&#xff0c;从而有效降低海量数据下C端访问数据库的压力。这种方式可以缓解单一数据库的压力&#xff0c;提升了吞吐量&#xff0c;但同时也带来了新的问题。对于B端商户而言&#…

赋能智能未来:AI大模型的学习之旅

随着人工智能的迅速发展&#xff0c;AI大模型已经成为技术领域的一个热点。这些模型以其强大的数据处理能力和预测精度&#xff0c;正在不断推动着科技的边界&#xff0c;并且在医疗、金融、交通等多个行业中显示出了巨大的潜力。然而&#xff0c;构建和训练一个高效的AI大模型…

C#非强签名dll搜索顺序

由于不是强签名dll&#xff0c;所以无效考虑全局程序集缓存 (GAC)。 预备工作 新建解决方案ClassLibrary1,新建类库ClassLibrary1,新建控制台程序ShowDllLoc。 利用VS添加引用。 一&#xff0c;利用app.config设置codebase&#xff0c;设置dll的加载路径为&#xff1a;code…

探索海外市场舆情:云手机助力企业赢得全球竞争

在全球化的趋势下&#xff0c;越来越多的企业将目光投向海外市场&#xff0c;迎接着无尽的商机与挑战。然而&#xff0c;随之而来的是境外市场舆情的复杂变化&#xff0c;对企业的声誉和发展带来了潜在风险。如何准确、及时地掌握境外市场的舆情动向&#xff0c;成为了企业必须…

RabbitMQ介绍及搭建

架构 RabbitMQ是实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff0c;使用erlang语言编写&#xff0c;依赖Erlang环境运行。 Broker&#xff1a;运行消息队列服务进程的节点&#xff0c;包含Exchange、Queue&#xff1b; Producer&#xff1a;消…