当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2023-10-08 07:05:59 173观看
导读大家好,我是哪吒。上一章提到了一个关于 i++ 和 ++i 的面试题打趴了所有人,最终方案是在两个方法上添加synchronized关键字,从而避免i++的线程安全问题,不过,这样真的好吗?在所有有线程安全的方法都添加synchronized?答案是

9rT28资讯网——每日最新资讯28at.com

大家好,我是哪吒。9rT28资讯网——每日最新资讯28at.com

上一章提到了一个关于 i++ 和 ++i 的面试题打趴了所有人,最终方案是在两个方法上添加synchronized关键字,从而避免i++的线程安全问题,不过,这样真的好吗?在所有有线程安全的方法都添加synchronized?9rT28资讯网——每日最新资讯28at.com

答案是显而易见的,不行。9rT28资讯网——每日最新资讯28at.com

synchronized会极大的降低程序的性能,导致整个程序几乎只能支持单线程操作,性能显著降低。9rT28资讯网——每日最新资讯28at.com

那么,如何解决呢?9rT28资讯网——每日最新资讯28at.com

一、降低锁的粒度,将synchronized关键字不放在方法上了,改为synchronized代码块。

锁的粒度更小了,也解决了这个问题,确实可以的。9rT28资讯网——每日最新资讯28at.com

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代码块变为区分读写场景的读写锁,也可以考虑悲观锁和乐观锁的区分。9rT28资讯网——每日最新资讯28at.com

对于读写场景比较多的情况,可以使用ReentrantReadWriteLock区分读写,再次降低锁的粒度,提高程序的性能。9rT28资讯网——每日最新资讯28at.com

ReentrantReadWriteLock 还可以选择提供了公平锁,在没有明确必须使用公平锁的情况下,尽量不要使用公平锁,公平锁会使程序性能降低很多很多。9rT28资讯网——每日最新资讯28at.com

二、先区分一下公平锁和非公平锁

  • 公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一个得到锁。
  • 非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,进入等待队列,如果能获取到,就直接获取到锁。

简单来说,公平锁(谁先排队,谁先执行),非公平锁(不用排队,每个人都有机会)。9rT28资讯网——每日最新资讯28at.com

1、公平锁

有一天早上,云韵、美杜莎、小医仙结伴去买酱香拿铁,到了咖啡店,先排队,一个一个来。不一会,哪吒来了,也买酱香拿铁,只能在末尾排队。这个就是公平锁。9rT28资讯网——每日最新资讯28at.com

9rT28资讯网——每日最新资讯28at.com

2、非公平锁

但是呢?第二天早上,哪吒又去买酱香拿铁,上一次去晚了没买到(线程被饿死了),这次急了,要插队买,不讲武德。终于喝上了心心念念的酱香拿铁,这个就是非公平锁。9rT28资讯网——每日最新资讯28at.com

9rT28资讯网——每日最新资讯28at.com

3、公平锁的优缺点:

  • 优点:所有线程都会获取到锁,只是一个时间的问题,不会出现有线程被饿死的情况;
  • 缺点:吞吐量会下降很多,队列里只有第一个线程能获取到锁,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会增大。

4、非公平锁的优缺点:

  • 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
  • 缺点:如果运气不好,会出现一致获取不到锁的情况,会被活活的饿死。

三、是否对症下药

我们都知道,静态字段属于类,类级别的锁才能保护;非静态字段属于类实例,实例级别的锁才能保护。9rT28资讯网——每日最新资讯28at.com

先看一下下面的代码:9rT28资讯网——每日最新资讯28at.com

import lombok.Data;import java.util.stream.IntStream;@Datapublic 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万次,测试一下。看看是否有线程安全的问题。9rT28资讯网——每日最新资讯28at.com

9rT28资讯网——每日最新资讯28at.com

这...不对啊,上一节在介绍高并发下i++线程安全问题的时候,synchronized 是好使的啊。9rT28资讯网——每日最新资讯28at.com

今天这是怎么了?再运行一次,结果依然如此,不等于1000009rT28资讯网——每日最新资讯28at.com

先来分析一下。9rT28资讯网——每日最新资讯28at.com

在非静态的方法上加synchronized,只能确保多个线程无法执行同一个实例的increase()方法,却不能保证不同实例的increase()方法。静态的变量time,在多个线程中共享,所以会出现线程安全的问题,synchronized失效了。9rT28资讯网——每日最新资讯28at.com

那么,将synchronized改为静态方法是不是就可以了,试一下。9rT28资讯网——每日最新资讯28at.com

有两种写法,一种是直接将方法改为静态方法,一种是使用synchronized代码块。9rT28资讯网——每日最新资讯28at.com

private static Object obj= new Object();public void increase() {    synchronized (obj) {        time++;    }}

9rT28资讯网——每日最新资讯28at.com

四、IntStream.rangeClosed是干嘛的?

很多小伙伴,可能会好奇,这个是干什么的,干了5年后端代码开发了,没见过这玩意儿。9rT28资讯网——每日最新资讯28at.com

IntStream是一种特殊的stream,用来提供对int相关的stream操作。9rT28资讯网——每日最新资讯28at.com

IntStream.rangeClosed:生成某个数字范围内的数字集合的stream。9rT28资讯网——每日最新资讯28at.com

比如上面代码中的IntStream.rangeClosed(1, 100000).parallel().forEach(i -> new LockTest().increase());。9rT28资讯网——每日最新资讯28at.com

  • range:不包含10000
  • rangeClosed:包含10000

五、parallel是干嘛的?

1、parallel()是什么

Stream.parallel() 方法是 Java 8 中 Stream API 提供的一种并行处理方式。在处理大量数据或者耗时操作时,使用 Stream.parallel() 方法可以充分利用多核 CPU 的优势,提高程序的性能。9rT28资讯网——每日最新资讯28at.com

Stream.parallel() 方法是将串行流转化为并行流的方法。通过该方法可以将大量数据划分为多个子任务交由多个线程并行处理,最终将各个子任务的计算结果合并得到最终结果。使用 Stream.parallel() 可以简化多线程编程,减少开发难度。9rT28资讯网——每日最新资讯28at.com

需要注意的是,并行处理可能会引入线程安全等问题,需要根据具体情况进行选择。9rT28资讯网——每日最新资讯28at.com

2、举一个简单的demo

定义一个list,然后通过parallel() 方法将集合转化为并行流,对每个元素进行i++,最后通过 collect(Collectors.toList()) 方法将结果转化为 List 集合。9rT28资讯网——每日最新资讯28at.com

使用并行处理可以充分利用多核 CPU 的优势,加快处理速度。9rT28资讯网——每日最新资讯28at.com

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);    }}

我勒个去,什么情况?9rT28资讯网——每日最新资讯28at.com

9rT28资讯网——每日最新资讯28at.com

这是大部分开发人员都会犯的小错误,在上篇中提到过,i++ 返回原来的值,++i 返回加1后的值。这谁都知道,可是,写的时候,就不一定了,因为你习惯了i++,写顺手了,写的时候也是心不在焉,一蹴而就了。9rT28资讯网——每日最新资讯28at.com

i++改了++i即可。9rT28资讯网——每日最新资讯28at.com

3、parallel()的优缺点

优点:9rT28资讯网——每日最新资讯28at.com

  • 充分利用多核 CPU 的优势,提高程序的性能;
  • 可以简化多线程编程,减少开发难度。

缺点:9rT28资讯网——每日最新资讯28at.com

  • 并行处理可能会引入线程安全等问题,需要根据具体情况进行选择;
  • 并行处理需要付出额外的开销,例如线程池的创建和销毁、线程切换等,对于小数据量和简单计算而言,串行处理可能更快。

4、何时使用parallel()?

在实际开发中,应该根据数据量、计算复杂度、硬件等因素综合考虑。9rT28资讯网——每日最新资讯28at.com

比如:9rT28资讯网——每日最新资讯28at.com

  • 数据量较大,有1万个元素;
  • 计算复杂度过大,需要对每个元素进行复杂的计算;
  • 硬件够硬,比如多核CPU。

9rT28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-12358-0.html简单聊一聊公平锁和非公平锁,Parallel并行流

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 你真的理解Python Qt6基础知识中的信号和槽机制吗?

下一篇: 有了这个代码模板,合并排序手到擒来

标签:
  • 热门焦点
  • 6月安卓手机性价比榜:Note 12 Turbo断层式碾压

    6月安卓手机性价比榜:Note 12 Turbo断层式碾压

    6月份有一个618,虽然这是京东周年庆的日子,但别的电商也都不约而同的跟进了,反正促销没坏处,厂商和用户都能满意。618期间一些产品也出现了历史低价,那么各个价位段的产品性价比
  • 把LangChain跑起来的三个方法

    把LangChain跑起来的三个方法

    使用LangChain开发LLM应用时,需要机器进行GLM部署,好多同学第一步就被劝退了,那么如何绕过这个步骤先学习LLM模型的应用,对Langchain进行快速上手?本片讲解3个把LangChain跑起来
  • Golang 中的 io 包详解:组合接口

    Golang 中的 io 包详解:组合接口

    io.ReadWriter// ReadWriter is the interface that groups the basic Read and Write methods.type ReadWriter interface { Reader Writer}是对Reader和Writer接口的组合,
  • 虚拟键盘 API 的妙用

    虚拟键盘 API 的妙用

    你是否在遇到过这样的问题:移动设备上有一个固定元素,当激活虚拟键盘时,该元素被隐藏在了键盘下方?多年来,这一直是 Web 上的默认行为,在本文中,我们将探讨这个问题、为什么会发生
  • ESG的面子与里子

    ESG的面子与里子

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之三伏大幕拉起,各地高温预警不绝,但处于厄尔尼诺大&ldquo;烤&rdquo;之下的除了众生,还有各大企业发布的ESG报告。ESG是&ldquo;环境保
  • 认真聊聊东方甄选:如何告别低垂的果实

    认真聊聊东方甄选:如何告别低垂的果实

    来源:山核桃作者:财经无忌爆火一年后,俞敏洪和他的东方甄选依旧是颇受外界关心的&ldquo;网红&rdquo;。7月5日至9日,为期5天的东方甄选&ldquo;甘肃行&rdquo;首次在自有App内直播,
  • 华为Mate60标准版细节曝光:经典星环相机模组回归

    华为Mate60标准版细节曝光:经典星环相机模组回归

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • 7月4日见!iQOO 11S官宣:“鸡血版”骁龙8 Gen2+200W快充加持

    7月4日见!iQOO 11S官宣:“鸡血版”骁龙8 Gen2+200W快充加持

    上半年已接近尾声,截至目前各大品牌旗下的顶级旗舰都已悉数亮相,而下半年即将推出的顶级旗舰已经成为了数码圈爆料的主流,其中就包括全新的iQOO 11S系
  • 英特尔Xe-HP项目终止,将专注Xe-HPC/HPG系列显卡

    英特尔Xe-HP项目终止,将专注Xe-HPC/HPG系列显卡

    据10 月 31 日消息报道,英特尔高级副总裁兼加速计算系统和图形事业部总经理 表示,Xe-HP“ Arctic Sound” 系列服务器 GPU 已经应用于 oneAPI devcloud 云服
Top