聊聊并发编程——原子操作类和Fork/Join框架

news2024/11/27 13:40:19

目录

原子操作类

实现原子性原理

保证原子性的方法

Fork/Join框架

分而治之

工作窃取算法

Fork/Join框架的设计

示例


原子操作类

线程A和线程B同时更新变量i进行操作i+1,最后的结果可能i不等于3而是等于2。这是线程不安全的更新操作,一般我们会使用Synchronized解决,但Java提供了更轻量级的选择——原子操作类:一种用法简单、性能高效、线程安全地更新一个变量的方法。

JUC下Atomic包一共提供了13个类,属于4中类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性:

  1. AtomicBoolean: 用于原子性地操作布尔值。

  2. AtomicInteger: 用于原子性地操作整数值。

  3. AtomicLong: 用于原子性地操作长整数值。

  4. AtomicReference: 用于原子性地操作引用类型。

  5. AtomicReferenceArray: 用于原子性地操作引用类型的数组。

  6. AtomicMarkableReference: 用于同时操作引用类型和布尔标志的原子引用。

  7. AtomicStampedReference: 用于同时操作引用类型和整数标志的原子引用。

  8. AtomicIntegerArray: 用于原子性地操作整数数组。

  9. AtomicLongArray: 用于原子性地操作长整数数组。

  10. AtomicReferenceFieldUpdater: 用于原子性地更新指定类中的字段。

  11. AtomicIntegerFieldUpdater: 用于原子性地更新指定类中的整数字段。

  12. AtomicLongFieldUpdater: 用于原子性地更新指定类中的长整数字段。

  13. AtomicAdder: Java 8 引入的类,用于原子性地执行加法操作。

实现原子性原理

一句话:通过Unsafe类使用CAS实现。

// AtomicInteger的添加方法
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    
    return var5;
}

CompareAndSwapInt是一个本地方法,基于CAS操作int类型变量。其他的原子操作基本都是大同小异。

保证原子性的方法
  • 使用原子操作类,如AtomicInteger实现i++原子操作

  • 使用JUC下的锁,如ReentrantLock,对i++操作加锁lock.loc()实现原子操作

  • 使用Synchronized,对i++操作加锁

Fork/Join框架

Fork/Join 框架是 Java 并发编程中的一个重要工具,用于并行处理任务,特别适用于分治算法。其中有两个关键概念:分而治之和工作窃取。

分而治之

将大任务划分为小任务,然后并行地执行这些小任务,最后将它们的结果合并。

工作窃取算法

在执行小任务的过程中,线程可以从其他线程的任务队列中窃取任务,从而保持线程的利用率。

  • 优点:充分利用线程进行并行计算,减少了线程间的竞争。

  • 缺点::在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并 且该算法会消耗了更多的系统资源,比如创建多个线程和多个双端队列。

Fork/Join框架的设计
  1. 分割任务。

    首先我们需要有一个fork类来把大任务分割成子任务,有可能子任务还是很大,所以还需要不停地分割,直到分割出的子任务足够小。

  2. 执行任务并合并结果

    分割的子任务分别放在双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里,启动一个线程 从队列里拿数据,然后合并这些数据。

Fork/Join使用两个类来完成以上两件事情:

  1. ForkJoinTask:我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务中执行fork()和join()操作的机制。通常情况下,我们不需要直接继承ForkJoinTask类,只需要继承它的子类,Fork/Join框架提供了以下两个子类。

    • RecursiveAction:用于没有返回结果的任务。

    • RecursiveTask:用于有返回结果的任务。

  2. ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行。

    任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任 务。

示例

使用Fork/Join框架计算1+2+3+4+5+6+7+8+9+10的结果。

  1. 如何分割任务?暂定单个子任务最多执行两个数相加,设置分割的阈值是2,那就是3个子任务,最后join3个子任务的结果。因为有结果,所以使用RecursiveTask。

    public class CountTask extends RecursiveTask<Integer> {
        // 设置阈值
        private static final int THRESHOLD = 2;
        private int start;
        private int end;
        public CountTask(int start, int end){
            this.start = start;
            this.end = end;
        }
    ​
        @Override
        protected Integer compute() {
            int sum = 0;
            // 如果任务足够小就计算任务
            boolean canCompute = (end - start) <= THRESHOLD;
            if (canCompute) {
                for (int i = start; i <= end ; i++) {
                    sum += i;
                }
            } else {
                // 如果任务大于阈值,就分裂为两个子任务计算
                int middle = (start + end) / 2;
                CountTask leftTask = new CountTask(start, middle);
                CountTask rightTask = new CountTask(middle + 1, end);
                // 执行子任务
                leftTask.fork();
                rightTask.fork();
                int leftResult = leftTask.join();
                int rightResult = rightTask.join();
                sum = leftResult + rightResult;
            }
            return sum;
        }
    ​
        public static void main(String[] args) {
            ForkJoinPool forkJoinPool = new ForkJoinPool();
            CountTask task = new CountTask(1, 10);
            Future<Integer> result = forkJoinPool.submit(task);
            try {
                System.out.println(result.get());
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    ForkJoinTask与一般任务的主要区别在于它需要实现compute方法进行任务分割。使用join方法会等待子任务执行完并得到其结果。

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

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

相关文章

CCF CSP认证 历年题目自练Day18

CCF CSP认证 历年题目自练Day18 题目一 试题编号&#xff1a; 201809-1 试题名称&#xff1a; 卖菜 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 256.0MB 问题描述&#xff1a; 问题描述   在一条街上有n个卖菜的商店&#xff0c;按1至n的顺序排成一排&#xff0c;这…

如何保持终身学习

文章目录 2.1. 了解你的大脑2.2 学习是对神经元网络的塑造2.3 大脑的一生 3.学习的心里基础3.1 固定思维与成长思维3.2 我们为什么要学习 4. 学习路径4.1 构建知识模块4.2 大脑是如何使用注意力的4.3 提高专注力4.4 放松一下&#xff0c;学的更好4.5 巩固你的学习痕迹4.6 被动学…

amazon自养号测评:为卖家提供稳定转化率的解决方案

亚马逊作为全球最大的跨境电商平台之一&#xff0c;吸引了大量卖家进入市场。然而&#xff0c;如何提高产品的转化率&#xff0c;吸引更多买家并促使他们下单&#xff0c;对卖家来说仍然是一个关键问题。本文将分享一些亚马逊卖家可以采用的小技巧&#xff0c;帮助他们实现这一…

Nginx之动静分离解读

目录 基本概念 基本入门 location匹配顺序 补充&#xff1a;URLRewrite 基本概念 动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来&#xff0c;动静资源做好了拆分以后&#xff0c;我们就可以根据静态资源的特点将其做缓存操作&#x…

2023八股每日一题(九月份)

9月13日 Q&#xff1a;JDK、JRE、JVM之间的区别 A&#xff1a; JDK(Java SE Development Kit)&#xff0c;Java标准开发包&#xff0c;它提供了编译、运⾏Java程序所需的各种⼯具和资源&#xff0c;包括Java编译器、Java运⾏时环境&#xff0c;以及常⽤的Java类库等JRE( Java…

jQuery入门学习

jQuery框架 jQuery是一个快速的、简洁的JavaScript框架&#xff08;库&#xff09;&#xff0c;它会封装很多JavaScript中常用的功能代码&#xff0c;提供了一个简洁的JS设计模式 优化HTML文档操作&#xff08;优化DOM操作&#xff09;事件处理动画设计Ajax 要使用JQ我们需要…

MySQL进阶_3.性能分析工具的使用

文章目录 第一节、数据库服务器的优化步骤第二节、查看系统性能参数第三节、 慢查询日志第四节、 查看 SQL 执行成本第五节、 分析查询语句&#xff1a;EXPLAIN 第一节、数据库服务器的优化步骤 当我们遇到数据库调优问题的时候&#xff0c;可以按照以下流程进行分析。整个流程…

【Java每日一题】— —第十八题:求二维数组中的元素最小值及其索引。(2023.10.02)

&#x1f578;️Hollow&#xff0c;各位小伙伴&#xff0c;今天我们要做的是第十八题。 &#x1f3af;问题&#xff1a; 求二维数组中的元素最小值及其索引。 测试结果如下&#xff1a; &#x1f3af; 答案&#xff1a; int [][]anew int[3][];a[0]new int [3];a[1]new int[5…

【Leetcode】 17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits "23" 输出&…

如何在 Wio Terminal 上运行 RT-Thread 操作系统

Wio Terminal 是 Seeed Studio 设计的一款开发套件。它基于 SAMD51 的微控制器&#xff0c;运行速度为 120MHz&#xff08;最高可达 200MHz&#xff09;&#xff0c;拥有 4MB 外部闪存和 192KB RAM&#xff0c;具有 Realtek RTL8720DN 支持的无线连接&#xff0c;同时支持蓝牙和…

京东数据报告:2023年8月京东手机行业品牌销售排行榜

鲸参谋监测的京东平台8月份手机市场销售数据已出炉&#xff01; 根据鲸参谋电商数据分析平台的数据显示&#xff0c;8月份&#xff0c;京东平台手机的销售量为380万&#xff0c;环比下滑约7%。同比下滑约17%&#xff1b;销售总额为120亿&#xff0c;环比下滑约17%&#xff0c;…

开启赏车新体验 远航汽车即将亮相2023中国(天津)国际汽车展览会

2023年9月28日至10月4日&#xff0c;2023中国&#xff08;天津&#xff09;国际汽车展览会将在国家会展中心&#xff08;天津&#xff09;举行。本次车展预计展出总面积20万平方米&#xff0c;是本年度北方地区规模最大、品牌最齐全的国际顶级车展。远航汽车将携旗下多款车型亮…

c#设计模式-结构型模式 之 装饰者模式

&#x1f680;介绍 在装饰者模式中&#xff0c;装饰者类通常对原始类的功能进行增强或减弱。这种模式是在不必改变原始类的情况下&#xff0c;动态地扩展一个对象的功能。这种类型的设计模式属于结构型模式&#xff0c;因为这种模式涉及到两个类型之间的关系&#xff0c;这两个…

Java编程技巧:分类

1、表结构 字段名称字段类型字段解释idvarchar主键idnamevarchar分类名称sequenceint同级排序parentvarchar父级分类id&#xff0c;一级分类的父级分类id为0pathvarchar分类id路径&#xff0c;中间用英文逗号,分隔&#xff0c;方便使用find_in_set函数搜索namePathvarchar分类…

90、Redis 的 value 所支持的数据类型(String、List、Set、Zset、Hash)---->Hash 相关命令

本次讲解要点&#xff1a; Hash 相关命令&#xff1a;是指value中的数据类型 启动redis服务器&#xff1a; 打开小黑窗&#xff1a; C:\Users\JH>e: E:>cd E:\install\Redis6.0\Redis-x64-6.0.14\bin E:\install\Redis6.0\Redis-x64-6.0.14\bin>redis-server.exe red…

Linux系统下C语言实现百度网盘(附实现步骤,和全部代码讲解)

Linux系统下C语言实现百度网盘 Linux操作系统下用C语言写一个网盘完整代码&#xff1a;服务器客户端 Linux操作系统下用C语言写一个网盘 本次实验完成了完整的网盘功能&#xff08;查询文件&#xff0c;下载文件&#xff0c;上传文件&#xff0c;刷新界面&#xff0c;和退出系…

服务器流量只有1tb,害怕被刷怎办,这篇文章教你防止对方刷流量!

本篇文章主要讲解&#xff0c;服务器流量监控和关闭网络请求的方法教程&#xff0c;在某种情况下可以有效杜绝被刷流量的困扰。 日期&#xff1a;2023年10月2日 作者&#xff1a;任聪聪 根本有效避免刷流的前置办法 说明&#xff1a;只选择固定带宽&#xff0c;不限流量的服务器…

【Linux进行时】进程地址空间

进程地址空间 例子引入&#xff1a; 我们在讲C语言的时候&#xff0c;老师给大家画过这样的空间布局图&#xff0c;但是我们对它不了解 我们写一个代码来验证Linux进程地址空间 #include<stdio.h> #include<assert.h> #include<unistd.h> int g_value100; …

【吞噬星空】连播两集,尼赫鲁对徐欣动手,罗峰修分身强势复仇

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析吞噬星空资讯。 吞噬星空动画第四季定档之后&#xff0c;官方真的是太宠粉了&#xff0c;每天都会公布全新预告情报&#xff0c;无论是外星人物角色&#xff0c;亦或者宇宙星球建模&#xff0c;那都是相当的炸裂。如今更…

《CTFshow-Web入门》10. Web 91~110

Web 入门 索引web91题解总结 web92题解总结 web93题解 web94题解 web95题解 web96题解 web97题解 web98题解 web99题解总结 web100题解 web101题解 web102题解 web103题解 web104题解 web105题解总结 web106题解 web107题解 web108题解 web109题解 web110题解 ctf - web入门 索…