多线程进阶:Callable和JUC的常见类

Callable

这是一个接口,类似于Runnable。

Runnable用来描述一个任务,描述的任务没有返回值。

Callable也是用来描述一个任务,描述的任务是有返回值的。

如果需要使用一个线程单独的计算出某个结果来,此时用Callable是比较合适的。

在new一个Callable之后,需要重写一个方法。就相当于是重写Runnable的Run方法,run方法的返回值是void,这里的call方法返回值是泛型参数。

 我们需要FutureTask的帮助:

 这个FutureTask就相当于一个未来的任务,类似于我们吃麻辣烫时,给我们叫号牌。等到麻辣烫做好后,会通过叫号牌来叫我们。

Future表示一个任务的周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务。

FutureTask实现了RunnableFuture接口,RunnableFuture接口又实现了Runnable接口和Future接口。所以FutureTask既可以被当做Runnable来执行,也可以被当做Future来获取Callable的返回结果

 和Runnable相比,Callable也是创建线程的一个方式,callable解决的是代码好不好看的问题,而不是结果对不对的问题。runnable也能得出结果,但是代码看起来比较乱。

ReentrantLock

这是标准库给我们提供的另一种锁,也是可重入锁。

synchronized是直接基于代码块的方式来加锁解锁的。

ReentrantLock更传统,使用了lock和unlock方法来加锁。

乍一看可能没什么问题,但是这样子加锁解锁可能会导致unlock执行不到。

那如果是lock后多用几个条件限制呢?

如果这中间存在return或者异常都可能导致unlock不能顺利执行~

建议的用法:

把unlock放到finally中。

try 关键字最后可以定义 finally 代码块。 finally 块中定义的代码,总是在 try 和任何 catch 块之后、方法完成之前运行。

正常情况下,不管是否抛出或捕获异常 finally 块都会执行。

 这样就能保证unlock一定会执行。

上面的是ReentrantLock的劣势,但是也是有优势的:

1.ReentrantLock提供了公平锁版本的实现

ReentrantLock reentrantLock = new ReentrantLock(true);

2.对于synchronized来说,提供的加锁操作就是死等,只要获取不到锁,就一直一直阻塞等待~

ReentrantLock提供了更灵活的等待方式:tryLock

reentrantLock.tryLock();

无参数版本,能加锁就加,加不上就放弃。

有参数版本,指定了超时时间,加不上锁就等一会,如果等一会时间到了也没加上就放弃等待。

3.ReentrantLock提供了一个更强大,更方便的等待通知机制。
synchronized搭配的是 wait notify。notify的时候是随机唤醒一个wait的线程。ReentrantLock搭配一个Condition类,进行唤醒的时候可以唤醒指定的线程。

总结:虽然ReentrantLock有一定的优势,但是实际开发中,大部分情况下还是用的synchronized。

原子类

原子类内部用的是CAS,所以性能要比加锁实现i++要高很多

虽然CAS确实是更加高效的解决了线程安全问题,但是CAS不能代替锁,CAS的适用范围是有限的,不像锁的适用范围那么广。

信号量 Semaphore

这里的信号量和操作系统上的信号量是同一个东西,只不过这里的信号量是Java把操作系统原生的信号量封装了一下。

信号量这个东西在我们生活中经常可以见到:

信号量就是这个计数器,描述了“可用资源的个数”

P操作:申请一个可用资源,计数器就要-1

V操作:释放一个可用资源,计数器就要+1

P操作如果要是计数器为0,继续P操作,就会出现阻塞操作。直到下一个V操作以后才能继续进行P操作。

实际开发中,虽然锁是最常用的,但是信号量也是会偶尔用到的,主要还是看实际的需求场景。

CountDownLatch

有一场跑步比赛,开始时间是明确的(裁判的发令枪),但是结束时间是不明确的(所有的选手都冲过终点线),为了等待这个跑步比赛结束,就引入了这个CountDownLatch。

有两个方法:

1.await(wait是等待,a =>all)主线程来调用这个方法

2.countDown表示选手冲过了终点线

例如,有四个选手进行比赛,初始情况下,调用await就会阻塞,就代表进入了比赛时间,每个选手冲过终点的时候,都会调用countDown方法。

前三次countDown,await没有任何影响,第四次调用countDown,await就会被唤醒,返回(解除阻塞),此时就可以认为是整个比赛都结束了。

多线程环境下使用ArrayList

1.自己加锁,使用synchronized或者ReentrantLock

2.Collections.synchronizedList 这里面会提供一些ArrayList相关的方法,同时是带锁的。

3.CopyOnWriteArrayList,简称为COW,也叫做写时拷贝。

针对这个ArrayList进行读操作,不做任何额外的工作;

如果进行写操作,则拷贝一份新的ArrayList,针对新的进行修改。修改的过程中如果有读的操作,那么就继续读旧的这一份数据。当修改完毕了,使用新的替换旧的。

这种方案优点:不需要加锁

              缺点:要求这个ArrayList不能太大,只是适用于数组比较小的情况下

多线程使用哈希表[重点、难点]

HashMap是线程不安全的,HashTable是线程安全的(因为给关键方案加了synchronized)

但是更推荐使用的是ConcurrentHashMap,这是更加优化的线程安全哈希表。

在这有几个重点:

1.ConcurrentHashMap进行了哪些优化?

2.比HashTable好在哪里?

3.和HashTable之间的区别是什么?

最大的优化之处:ConcurrentHashMap相比于HashTable大大缩小了锁冲突的概率,把一把大锁,转换成多把小锁了。

HashTable会在整个链表上加锁。 

ConcurrentHashMap的做法是,每个链表有各自的锁,而不是共用一个锁

具体来说,就是用每个链表的头结点,作为锁对象。这样两个线程不针对同一个对象加锁,就不会有锁竞争。

JDK1.7和之前

但是呢,ConcurrentHashMap做了一个激进的操作:

针对读操作不加锁,只针对锁操作加锁。

并且,ConcurrentHashMap内部充分利用了CAS,通过这个来进一步削减加锁操作的数目。

针对扩容,采取“化整为零”的方式

HashMap/HashTable扩容:

创建一个更大的数组空间,把旧的数组上的链表上的每个元素搬运到新的数组上(删除+插入)

这个扩容操作会在某次put的时候进行触发,如果元素个数特别多,就会导致这样的搬运操作比较耗时。(比如某次put的时候,某个用户就卡了)

ConcurrentHashMap扩容:

每次搬运一小部分元素,创建新的数组,旧的数组也保留。每次put操作,都往新数组上添加,同时进行一部分搬运(把一小部分旧的元素搬运到新数组上)

每次get的时候,把新旧数组都查询,remove的时候,把旧数组的元素删了就行了

经过一段时间之后,所有的元素都搬运好了,再释放旧数组。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【好玩的开源项目】Windows 12网页版的部署与使用体验

【好玩的开源项目】Windows 12网页版的部署与使用体验 一、Windows 12网页版介绍1.1 Windows 12网页版简介1.2 项目地址 二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍 三、安装httpd软件3.1 检查yum仓库3.2 安装httpd软件3.3 启动httpd服务3.4 查看httpd服务3.5 防火墙和…

springboot+canal+mysql+redis缓存双写一致性

canal官网地址:https://github.com/alibaba/canal/wiki/QuickStart 基本上按照官网的步骤来就行 准备 首先服务器上要安装好jdk,因为canal运行需要jdk,同时把canal对应的端口在服务中开放,否则连接不上 对于自建 MySQL , 需要先开启 Binl…

QT-day5

1、添加注册功能到数据库 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMessageBox> //消息对话框类头文件 #include <QDebug> #include <QPushButton> #include <QSqlDatabase> //数据库管理类 #include…

ChatGPT详细搭建教程+支持AI绘画

一、AI创作系统 SparkAi系统是基于很火的GPT提问进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT系统&#xff1f;小编这里写一个详细图文教程吧&#x…

SpringBoot 之配置加密

Jasypt库的使用 Jasypt是一个Java简易加密库&#xff0c;用于加密配置文件中的敏感信息&#xff0c;如数据库密码。 Jasypt库与springboot集成&#xff0c;在实际开发中非常方便。 1、引入依赖 <dependency><groupId>com.github.ulisesbocchio</groupId>&…

构建工具Webpack简介

一、构建工具 当我们习惯了Node中使用ES模块化编写代码以后&#xff0c;用原生的HTML、CSS、JS这些东西会感觉到各种不便。比如&#xff1a;不能放心的使用模块化规范&#xff08;浏览器兼容性问题&#xff09;、即使可以使用模块化规范也会面临模块过多时的加载问题。 这时候…

当当网商品详情数据接口

当当网商品详情数据接口可以通过当当网的开放平台获取相关信息。您可以注册当当开放平台账号&#xff0c;并按照要求提交申请获取API接口的调用凭证。获得授权后&#xff0c;您将会收到一组AccessKey和SecretKey。使用编程语言&#xff08;如Java&#xff09;调用API接口&#…

《计算机网络》——应用层

2.1 应用层协议原理&#xff08;P54&#xff09; 研发网络应用的核心是写出能够运行在不同端系统和通过网络彼此交流的程序。 2.1.1 网络应用程序体系结构 两种主流的应用体系结构&#xff1a;客户-服务器体系结构、对等体系结构。 客户-服务器体系&#xff1a;服务器是一个…

适合新手自学的网络安全基础技能“蓝宝书”:《CTF那些事儿》

CTF比赛是快速提升网络安全实战技能的重要途径&#xff0c;已成为各个行业选拔网络安全人才的通用方法。但是&#xff0c;本书作者在从事CTF培训的过程中&#xff0c;发现存在几个突出的问题&#xff1a; 1&#xff09;线下CTF比赛培训中存在严重的“最后一公里”问题&#xf…

【项目实战】Linux系统下jar包自启动

什么是jar包自启动 在Linux系统中&#xff0c;"jar包自启动"是指通过配置将Java程序打包成可执行的Jar文件&#xff0c;并设置其在系统启动时自动运行。以下是与jar包自启动相关的一些概念&#xff1a; Jar文件&#xff1a;Jar&#xff08;Java Archive&#xff09…

51单片机光照强度检测自动路灯开关仿真( proteus仿真+程序+报告+讲解视频)

51单片机光照强度检测自动路灯开关仿真( proteus仿真程序报告讲解视频&#xff09; 仿真图proteus7.8及以上 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;S0052 讲解视频 基于51单片机的光照检测自动路灯控制仿真设计( proteus仿…

Day56:组件库封装-TypeScript入门

配置 安装tsc工具进行编译 npm i typescript -g 查看版本号&#xff1a;tsc -v 编译ts代码-需要使用tsc编译之后才能运行&#xff0c;TS为JS的衍生&#xff0c;浏览器不能直接识别TS语法&#xff1a;tsc xxx.ts 运行ts代码&#xff1a;node xxx.js 或者直接运行ts代码——t…

【从0学习Solidity】52. EIP712 类型化数据签名

【从0学习Solidity】 52. EIP712 类型化数据签名 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#…

ChatGPT的问世给哪些行业带来了冲击?

目录 引言Chat GPT 对行业的影响在线客服和智能客服行业传统自动回复机器人的局限性Chat GPT 的提升能力 教育培训行业个性化学习需求的挑战Chat GPT 的个性化优势 金融保险行业客户服务的变革Chat GPT 的智能化应用 医疗健康领域自助诊断及咨询的便利性Chat GPT 在医疗领域的应…

vue项目打包部署到服务器,报错。

这个是因为后端部署服务器时&#xff0c;名称没有对上&#xff0c;不是前端的问题&#xff0c;后端配置名称和前端的包名称保持一致就可以了。

轻量级c语言开源日志库log.c介绍 - 实现不同级别和参数化日志打印

前言 c语言没有现成的日志库&#xff0c;如果要记录日志&#xff0c;需要自己封装一个日志库。如果要实现日志级别和参数打印&#xff0c;还是比较麻烦的&#xff0c;正好在github找到了一个c语言开源日志库&#xff0c;可以实现日志级别打印&#xff0c;参数打印&#xff0c;…

SAP PO运维(四):适配器消息监控

登录SAP PO系统,点击“Configuration and Monitoring Home”,使用PISUPER账号登录: 2、选择“适配器引擎->消息监控器”: 3、查看是否有报错消息: 双击报错的数字,筛选出报错的条目(可以根据状态、接口命名空间等来筛选):常见的报错消息有: 接口配置问题:字段为空值…

好题记录 Leetcode 394.字符串解码 中等难度

方法一&#xff1a;递归 思路很简单&#xff0c;比较好理解&#xff0c;注意细节处理&#xff01;&#xff01;&#xff01; class Solution { public:string decodeString(string s) {string ans;for(int i0;s[i]!0;i){if(s[i]>a&&s[i]<z)anss[i];if(s[i]>…

十四、流式编程(4)

本章概要 终端操作 数组循环集合组合匹配查找信息数字流信息 终端操作 以下操作将会获取流的最终结果。至此我们无法再继续往后传递流。可以说&#xff0c;终端操作&#xff08;Terminal Operations&#xff09;总是我们在流管道中所做的最后一件事。 数组 toArray()&…

实时更新进度条:JavaScript中的定时器和异步编程技巧

前言 在Web开发中&#xff0c;有许多场景需要实时地更新页面上的进度&#xff0c;例如上传文件、数据处理等。本文将介绍如何利用JavaScript中的定时器和异步编程技巧来实现实时更新进度&#xff0c;并探讨一些其他解决方案。 处理进度实时更新&#xff1a; 利用异步编程实现实…