简单聊一聊公平锁和非公平锁,parallel并行流

目录
- 一、降低锁的粒度,将synchronized关键字不放在方法上了,改为synchronized代码块。
- 二、先区分一下公平锁和非公平锁
- 1、公平锁
- 2、非公平锁
- 3、公平锁的优缺点:
- 4、非公平锁的优缺点:
- 三、是否对症下药
- 四、IntStream.rangeClosed是干嘛的?
- 五、parallel是干嘛的?
- 1、parallel()是什么
- 2、举一个简单的demo
- 3、parallel()的优缺点
- 4、何时使用parallel()?
大家好,我是哪吒。
上一章提到了i++的线程安全问题,最终方案是在两个方法上添加synchronized关键字,从而避免i++的线程安全问题,不过,这样真的好吗?在所有有线程安全的方法都添加synchronized?
答案是显而易见的,不行。
synchronized会极大的降低程序的性能,导致整个程序几乎只能支持单线程操作,性能显著降低。
那么,如何解决呢?
一、降低锁的粒度,将synchronized关键字不放在方法上了,改为synchronized代码块。
锁的粒度更小了,也解决了这个问题,确实可以的。
package com.guor.thread;public class SynchronizedTest2 {int a = 1;int b = 1;public void add() {System.out.println("add start");synchronized (this) {for (int i = 0; i < 10000; i++) {a++;b++;}}System.out.println("add end");}public synchronized void compare() {System.out.println("compare start");synchronized (this) {for (int i = 0; i < 10000; i++) {boolean flag = a < b;if (flag) {System.out.println("a=" + a + ",b=" + b + "flag=" + flag + ",a < b = " + (a < b));}}}System.out.println("compare end");}public static void main(String[] args) {SynchronizedTest2 synchronizedTest = new SynchronizedTest2();new Thread(() -> synchronizedTest.add()).start();new Thread(() -> synchronizedTest.compare()).start();}
}
为了更好的优化,有的时候可以将synchronized代码块变为区分读写场景的读写锁,也可以考虑悲观锁和乐观锁的区分。
对于读写场景比较多的情况,可以使用ReentrantReadWriteLock区分读写,再次降低锁的粒度,提高程序的性能。
ReentrantReadWriteLock 还可以选择提供了公平锁,在没有明确必须使用公平锁的情况下,尽量不要使用公平锁,公平锁会使程序性能降低很多很多。
二、先区分一下公平锁和非公平锁
- 公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一个得到锁。
- 非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,进入等待队列,如果能获取到,就直接获取到锁。
简单来说,公平锁(谁先排队,谁先执行),非公平锁(不用排队,每个人都有机会)。
1、公平锁
有一天早上,云韵、美杜莎、小医仙结伴去买酱香拿铁,到了咖啡店,先排队,一个一个来。不一会,哪吒来了,也买酱香拿铁,只能在末尾排队。这个就是公平锁。

2、非公平锁
但是呢?第二天早上,哪吒又去买酱香拿铁,上一次去晚了没买到(线程被饿死了),这次急了,要插队买,不讲武德。终于喝上了心心念念的酱香拿铁,这个就是非公平锁。

3、公平锁的优缺点:
- 优点:所有线程都会获取到锁,只是一个时间的问题,不会出现有线程被饿死的情况;
- 缺点:吞吐量会下降很多,队列里只有第一个线程能获取到锁,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会增大。
4、非公平锁的优缺点:
- 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
- 缺点:如果运气不好,会出现一致获取不到锁的情况,会被活活的饿死。
三、是否对症下药
我们都知道,静态字段属于类,类级别的锁才能保护;非静态字段属于类实例,实例级别的锁才能保护。
先看一下下面的代码:
import lombok.Data;import java.util.stream.IntStream;@Data
public class LockTest {public static void main(String[] args) {IntStream.rangeClosed(1, 100000).parallel().forEach(i -> new LockTest().increase());System.out.println(time);}private static int time = 0;public synchronized void increase() {time++;}
}
在LockTest类中定义一个静态变量time,定义一个非静态方法increase(),实现time++自增。先累加10万次,测试一下。看看是否有线程安全的问题。

这…不对啊,上一节在介绍高并发下i++线程安全问题的时候,synchronized 是好使的啊。
今天这是怎么了?再运行一次,结果依然如此,不等于100000
先来分析一下。
在非静态的方法上加synchronized,只能确保多个线程无法执行同一个实例的increase()方法,却不能保证不同实例的increase()方法。
静态的变量time,在多个线程中共享,所以会出现线程安全的问题,synchronized失效了。
那么,将synchronized改为静态方法是不是就可以了,试一下。
有两种写法,一种是直接将方法改为静态方法,一种是使用synchronized代码块。
private static Object obj= new Object();
public void increase() {synchronized (obj) {time++;}
}

四、IntStream.rangeClosed是干嘛的?
很多小伙伴,可能会好奇,这个是干什么的,干了5年后端代码开发了,没见过这玩意儿。
IntStream是一种特殊的stream,用来提供对int相关的stream操作。
IntStream.rangeClosed:生成某个数字范围内的数字集合的stream。
比如上面代码中的IntStream.rangeClosed(1, 100000).parallel().forEach(i -> new LockTest().increase());。
- range:不包含10000
- rangeClosed:包含10000
关于Java8 新特性 Stream流的详细介绍,可以看一下这个【Java8 新特性 5】Java 8 stream的详细用法。
五、parallel是干嘛的?
1、parallel()是什么
Stream.parallel() 方法是 Java 8 中 Stream API 提供的一种并行处理方式。在处理大量数据或者耗时操作时,使用 Stream.parallel() 方法可以充分利用多核 CPU 的优势,提高程序的性能。
Stream.parallel() 方法是将串行流转化为并行流的方法。通过该方法可以将大量数据划分为多个子任务交由多个线程并行处理,最终将各个子任务的计算结果合并得到最终结果。使用 Stream.parallel() 可以简化多线程编程,减少开发难度。
需要注意的是,并行处理可能会引入线程安全等问题,需要根据具体情况进行选择。
2、举一个简单的demo
定义一个list,然后通过parallel() 方法将集合转化为并行流,对每个元素进行i++,最后通过 collect(Collectors.toList()) 方法将结果转化为 List 集合。
使用并行处理可以充分利用多核 CPU 的优势,加快处理速度。
public class StreamTest {public static void main(String[] args) {List<Integer> list = new ArrayList<>();for (int i = 0; i < 10; i++) {list.add(i);}System.out.println(list);List<Integer> result = list.stream().parallel().map(i -> i++).collect(Collectors.toList());System.out.println(result);}
}
我勒个去,什么情况?

这是大部分开发人员都会犯的小错误,在上篇中提到过,i++ 返回原来的值,++i 返回加1后的值。这谁都知道,可是,写的时候,就不一定了,因为你习惯了i++,写顺手了,写的时候也是心不在焉,一蹴而就了。
i++改了++i即可。
3、parallel()的优缺点
(1)优点:
- 充分利用多核 CPU 的优势,提高程序的性能;
- 可以简化多线程编程,减少开发难度。
(2)缺点:
- 并行处理可能会引入线程安全等问题,需要根据具体情况进行选择;
- 并行处理需要付出额外的开销,例如线程池的创建和销毁、线程切换等,对于小数据量和简单计算而言,串行处理可能更快。
4、何时使用parallel()?
在实际开发中,应该根据数据量、计算复杂度、硬件等因素综合考虑。
比如:
- 数据量较大,有1万个元素;
- 计算复杂度过大,需要对每个元素进行复杂的计算;
- 硬件够硬,比如多核CPU。
上一篇:一个关于 i++ 和 ++i 的面试题打趴了所有人
🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。
相关文章:
简单聊一聊公平锁和非公平锁,parallel并行流
目录 一、降低锁的粒度,将synchronized关键字不放在方法上了,改为synchronized代码块。二、先区分一下公平锁和非公平锁1、公平锁2、非公平锁3、公平锁的优缺点:4、非公平锁的优缺点: 三、是否对症下药四、IntStream.rangeClosed是…...
【SpringCloud】微服务技术栈入门4 - RabbitMQ初探
目录 RabbitMQ安装 rabbitmqSpringAMQP 基础队列WorkQueue路由发布订阅 FanoutExchangeDirectExchangeTopicExchange RabbitMQ 安装 rabbitmq 首先确保自己已经安装好了 docker 是 docker 拉取镜像文件:docker pull rabbitmq:3-management 拉取完毕,打…...
cefsharp(117.2.20)cef117.2.2最新体验版
一、下载nupkg https://www.nuget.org/packages/CefSharp.WinForms/ https://www.nuget.org/packages/CefSharp.Common/ https://www.nuget.org/packages/cef.redist.x64/ https://www.nuget.org/packages/cef.redist.x86/ 此版本暂时不支持H264。上一版本支持H264 cefsharp…...
layui在上传图片在前端处理图片压缩
有的人会遇到需要在前端代码处理图片压缩的问题,下面给大家分享怎么处理。 // 上传图片 var image_src var IsImgDealfalse; layui.upload.render({ elem: "#{tag}{id}", url: sessionStorage.getItem(httpUrlPrefix) /upload/uploadImage, // dataT…...
js 事件参考
事件参考 事件介绍 触发事件是为了通知代码可能影响代码执行的“有趣变化”。这些可能来自用户交互,例如使用鼠标或调整窗口大小,底层环境状态的变化(例如,低电量或来自操作系统的媒体事件)以及其他原因。 每个事件都由一个基于Event接口的…...
卷积网络的发展历史-LeNet
简介 LeNet是CNN结构的开山鼻祖,第一次定义了卷积神经网络的结构。 LeNet模型包含了多个卷积层和池化层,以及最后的全连接层用于分类。其中,每个卷积层都包含了一个卷积操作和一个非线性激活函数,用于提取输入图像的特征。池化层…...
(2023,GPT-4V,LLM,LMM,功能和应用)大型多模态模型的黎明:GPT-4V(ision) 的初步探索
The Dawn of LMMs: Preliminary Explorations with GPT-4V(ision) 公众号:EDPJ(添加 VX:CV_EDPJ 或直接进 Q 交流群:922230617 获取资料) 目录 0. 摘要 1. 简介 1.1 动机和概述 1.2 我们探索 GPT-4V 的方法 1.3…...
【C++设计模式之装饰模式:结构型】分析及示例
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地给一个对象添加额外的行为。 描述 装饰模式通过创建一个包装器(Wrapper)来包裹原始对象,并在原始对象的行为前后添加额外的功能。…...
绘制散点图、曲线图、折线图和环形图失败, 设置迭代次数和进度无法保存图片
错误❌ 分别input设置(我想知道微积分的力量) 设1个人,他有每天3种方案,每天进步千分之一,千分之一,十万分之一等到他们迭代 200,500,1000,2000,3000,5000,9000次 他们在图片什么位置画曲线图࿰…...
micro-ROS中对消息的内存管理
文章目录 1.背景2.答案2.1.基本类型及其数组,不需要2.1.序列类型(复合类型、复合序列类型),需要 3.内存申请方法3.1.手动申请(Manual allocation)3.1.工具辅助(micro-ROS utilities)…...
Springboot中使用拦截器、过滤器、监听器
一、Servlet、Filter(过滤器)、 Listener(监听器)、Interceptor(拦截器) Javaweb三大组件:servlet、Filter(过滤器)、 Listener(监听器) Spring…...
代码随想录二刷day45
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣70. 爬楼梯二、力扣322. 零钱兑换三、力扣279. 完全平方数 前言 一、力扣70. 爬楼梯 class Solution {public int climbStairs(int n) {int[] dp new…...
泊车功能专题介绍 ———— AVP系统基础数据交互内容
文章目录 系统架构系统功能描述云端子系统车辆子系统场端子系统用户APP 工作流程基础数据交互内容AVP 系统基础数据交互服务车/用户 - 云基础数据交互内容车位查询工作流程技术要求数据交互要求 车位预约工作流程技术要求数据交互要求 取消预约工作流程技术要求数据交互要求 泊…...
蓝桥杯每日一题2023.10.6
题目描述 门牌制作 - 蓝桥云课 (lanqiao.cn) 题目分析 #include<bits/stdc.h> using namespace std; int ans; int main() {for(int i 1; i < 2020; i ){int x i;while(x){int a x % 10;if(a 2)ans ;x / 10;}}cout << ans;return 0; } 题目描述 既约分数…...
7、【Qlib】【主要组件】Data Layer:数据框架与使用
7、【主要组件】Data Layer:数据框架与使用 简介数据准备Qlib 格式数据Qlib 格式数据集自动更新日频率数据将 CSV 格式转换为 Qlib 格式股票池(市场)多股票模式 数据API数据检索特征过滤器 数据加载器QlibDataLoaderStaticDataLoaderInterfac…...
Kubernetes安装部署 1
本文主要描述kubernetes的安装部署,kubernetes的安装部署主要包括三个关键组件,其中,包括kubeadm、kubelet、kubectl,这三个组件的功能描述如下所示: Kubeadm 用于启动与管理kubernetes集群 Kubelet 运行在所有集群的…...
在VS Code中优雅地编辑csv文件
文章目录 Rainbow csv转表格CSV to Tablecsv2tableCSV to Markdown Table Edit csv 下面这些插件对csv/tsv/psv都有着不错的支持,这几种格式的主要区别是分隔符不同。 功能入口/使用方法Rainbow csv按列赋色右键菜单CSV to Table转为ASCII表格指令CSV to Markdown …...
LCR 128.库存管理 I
题目来源: leetcode题目,网址:LCR 128. 库存管理 I - 力扣(LeetCode) 解题思路: 数组可以分割成两段的升序连续子数组,找到两个子数组的开始元素并返回较小者即可。 解题代码: …...
eigen::Affine3d 转换
平移eigen::vector3d和四元数Eigen::Quaterniond 转 eigen::Affine3d Eigen::Vector3d t Eigen::Vector3d::Zero(); Eigen::Quaterniond q Eigen::Quaterniond ::Identity();Eigen::Affine3d affine3d t * q.toRotationMatrix(); Eigen::Matrix4d 转 eigen::Affine3d Eige…...
【Python从入门到进阶】38、selenium关于Chrome handless的基本使用
接上篇《37、selenium关于phantomjs的基本使用》 上一篇我们介绍了有关phantomjs的相关知识,但由于selenium已经放弃PhantomJS,本篇我们来学习Chrome的无头版浏览器Chrome Handless的使用。 一、Chrome Headless简介 Chrome Headless是一个无界面的浏览…...
vLLM-v0.17.1保姆级教程:SSH中查看vLLM实时请求队列与Pending统计
vLLM-v0.17.1保姆级教程:SSH中查看vLLM实时请求队列与Pending统计 1. vLLM框架简介 vLLM是一个专注于大语言模型(LLM)推理和服务的高性能库,它的设计目标是让开发者能够轻松部署和管理大规模语言模型。这个项目最初由加州大学伯克利分校的天空计算实验…...
三层架构破解小红书数据采集难题:Appium+MitmProxy双引擎实战
三层架构破解小红书数据采集难题:AppiumMitmProxy双引擎实战 【免费下载链接】XiaohongshuSpider 小红书爬取 项目地址: https://gitcode.com/gh_mirrors/xia/XiaohongshuSpider 在小红书内容生态快速发展的今天,数据工程师和产品分析师面临着内容…...
OpenClaw+GLM-4.7-Flash成本对比:自建模型比API调用节省30%token消耗
OpenClawGLM-4.7-Flash成本对比:自建模型比API调用节省30%token消耗 1. 为什么需要关注token消耗 上周五凌晨两点,我的OpenClaw突然停止了周报自动化任务。查看日志发现是API额度耗尽——当月累计消耗已超过商用GLM-4.7-Flash的套餐限额。这次意外让我…...
Greasy Fork:用户脚本管理的一站式开源解决方案
Greasy Fork:用户脚本管理的一站式开源解决方案 【免费下载链接】greasyfork An online repository of user scripts. 项目地址: https://gitcode.com/gh_mirrors/gr/greasyfork 从脚本新手到社区贡献者的进阶指南 一、功能探索:解锁浏览器增强新…...
水墨江南模型Agent智能体开发:自主中式艺术创作助手
水墨江南模型Agent智能体开发:自主中式艺术创作助手 最近在捣鼓AI绘画,发现一个挺有意思的事儿。很多朋友想用AI画点有中国风味的作品,比如水墨画、山水画,但往往折腾半天,出来的效果总差那么点意思。要么是意境不对&…...
NaViL-9B多模态模型5分钟快速部署:图文问答零基础入门教程
NaViL-9B多模态模型5分钟快速部署:图文问答零基础入门教程 1. 认识NaViL-9B多模态模型 NaViL-9B是上海人工智能实验室推出的原生多模态大语言模型,它不仅能像传统语言模型一样处理纯文本问答,还具备强大的图片理解能力。这意味着你可以上传…...
告别硬编码!用BAdI LE_SHP_TAB_CUST_ITEM给VL01N交货单加个自定义标签页(附完整代码)
告别硬编码!用BAdI LE_SHP_TAB_CUST_ITEM实现交货单标签页优雅增强 在SAP SD模块的日常开发中,为外向交货单添加客户化信息是再常见不过的需求。但很多开发者习惯直接在标准程序里插入硬编码,这种看似快捷的方式往往会给后续维护埋下隐患。今…...
闽北哥-柔弱胜刚强:真正的强者,从不硬碰
柔弱胜刚强 ——真正的强者,从不硬碰“为什么真正厉害的人, 看起来都有些柔弱?🌿 因为—— 刚强自毁,柔弱长存。🔮 这不是权谋, 而是—— 天地运行的铁律。”🌊 一、误解千年&#x…...
LFM2.5-1.2B-Thinking-GGUF前端面试题解析实战:模拟面试与答案生成
LFM2.5-1.2B-Thinking-GGUF前端面试题解析实战:模拟面试与答案生成 1. 开篇:AI如何改变前端面试准备方式 前端开发岗位的竞争日益激烈,技术面试的难度也水涨船高。传统的面试准备方式往往效率低下——求职者要么死记硬背网上的标准答案&…...
从零到一:基于LLaMA-Factory的微调实战与核心参数精讲
1. 环境准备与LLaMA-Factory初探 第一次接触LLaMA-Factory时,我对着官方文档发呆了半小时——这个工具链实在太强大了,但新手很容易被各种依赖项劝退。这里分享我的踩坑经验:不要一上来就追求最新版本。去年12月我在RTX 3090上折腾v0.4.0时&a…...
