并发和并行的区别

news2024/11/15 8:51:13

目录

    • 背景
    • 过程
      • 例子:
      • 定义:
      • 并发解决办法(Redisson):
      • 解决并发步骤(Redisson):
    • 总结

背景

在设计Arpro第三版的时候马总提出了一个问题,我们认为人家表达是并发问题,但是由于并发边界不清晰,对这个问题没有详细的认知。

过程

例子:

并发的例子:
假设有一个多用户的聊天应用程序,多个用户可以同时发送消息并接收消息。当用户A发送一条消息时,用户B也可以同时发送另一条消息。这些消息的发送和接收是并发执行的,它们可以交替进行,但每个用户的操作都可能在任意时刻被暂停,切换到其他用户的操作,然后再恢复执行。这里的关键是多个用户的操作可以同时进行,但并不一定是同时完成。

并行的例子:
假设有一个图像处理应用程序,需要对一张大图进行处理,包括图像分割、滤镜处理和图像合并。如果使用并行处理,可以将图像分成多个块,然后分配给多个处理器核心同时进行处理。每个处理器核心独立处理一个图像块,最后将处理结果合并成最终的图像。在这个例子中,不同的图像块可以并行地进行处理,每个处理器核心负责一个块的处理,从而加速了整体的图像处理过程。

定义:

并发是指多个任务在同一时间段内执行,并且这些任务可以交替执行,每个任务都有可能在任意时刻暂停、切换到其他任务,然后再恢复执行。并行是指多个任务同时执行,每个任务都在不同的处理器核心或计算资源上独立运行,彼此之间相互独立,不会相互干扰。

并发和并行的区别在于任务的执行方式和资源的分配方式。并发注重任务的交替执行和资源的共享,通过合理的调度和同步机制来实现高效的任务处理;而并行注重任务的同时执行和资源的分配,通过利用多个处理器核心或计算资源来实现任务的加速。

并发解决办法(Redisson):

Redisson是一个Java库,提供了在分布式系统中处理并发访问的简单而高效的方法。它使用Redis作为后端存储,并提供了一系列分布式数据结构和同步机制,可以在分布式环境中处理并发访问。

通过Redisson,您可以使用分布式锁来协调对共享资源的访问,确保只有一个线程或进程可以同时访问该资源。这样可以避免多个线程或进程同时修改共享资源导致的数据一致性问题。

此外,Redisson还提供了分布式的原子操作、分布式集合和分布式消息传递等功能,可以帮助您在分布式系统中进行并发控制和协作。通过使用Redisson提供的这些功能,您可以有效地处理并发访问共享资源的问题。

解决并发步骤(Redisson):

  <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.17.4</version>
        </dependency>
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//


import java.util.ArrayList;
import java.util.Iterator;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.BaseConfig;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.util.StringUtils;

@Configuration
@ConditionalOnMissingBean({RedissonClient.class})
@Import({RedissonConfig.class})
public class RedissonClientConfig {
    private final Config config;

    RedissonClientConfig(Config config) {
        this.config = config;
    }

    @Bean
    public RedissonClient redissonClient() {
        return Redisson.create(this.config);
    }

    @ConditionalOnMissingBean({Config.class})
    @EnableConfigurationProperties({RedisProperties.class})
    static class RedissonConfig {
        @Autowired
        private RedisProperties redisProperties;

        RedissonConfig() {
        }

        @Bean
        public Config redissonConfig() {
            Config config = new Config();
            ArrayList clusterNodes;
            Iterator var5;
            String node;
            if (this.redisProperties.getSentinel() != null) {
                SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
                RedisProperties.Sentinel sentinel = this.redisProperties.getSentinel();
                sentinelServersConfig.setMasterName(sentinel.getMaster());
                clusterNodes = new ArrayList();
                var5 = sentinel.getNodes().iterator();

                while(var5.hasNext()) {
                    node = (String)var5.next();
                    clusterNodes.add("redis://" + node);
                }

                sentinelServersConfig.addSentinelAddress((String[])clusterNodes.toArray(new String[clusterNodes.size()]));
                sentinelServersConfig.setDatabase(this.redisProperties.getDatabase());
                sentinelServersConfig.setPingConnectionInterval(60000);
                this.baseConfig(sentinelServersConfig, this.redisProperties);
            } else if (this.redisProperties.getCluster() != null) {
                ClusterServersConfig clusterServersConfig = config.useClusterServers();
                RedisProperties.Cluster cluster = this.redisProperties.getCluster();
                clusterNodes = new ArrayList();
                var5 = cluster.getNodes().iterator();

                while(var5.hasNext()) {
                    node = (String)var5.next();
                    clusterNodes.add("redis://" + node);
                }

                clusterServersConfig.addNodeAddress((String[])clusterNodes.toArray(new String[cluster.getNodes().size()]));
                clusterServersConfig.setFailedSlaveReconnectionInterval(cluster.getMaxRedirects());
                clusterServersConfig.setPingConnectionInterval(6000);
                this.baseConfig(clusterServersConfig, this.redisProperties);
            } else {
                SingleServerConfig singleServerConfig = config.useSingleServer();
                String schema = this.redisProperties.isSsl() ? "rediss://" : "redis://";
                singleServerConfig.setAddress(schema + this.redisProperties.getHost() + ":" + this.redisProperties.getPort());
                singleServerConfig.setDatabase(this.redisProperties.getDatabase());
                singleServerConfig.setPingConnectionInterval(60000);
                this.baseConfig(singleServerConfig, this.redisProperties);
            }

            config.setCodec(new JsonJacksonCodec());
            config.setLockWatchdogTimeout(30000L);
            return config;
        }

        private void baseConfig(BaseConfig config, RedisProperties properties) {
            if (!StringUtils.isEmpty(properties.getPassword())) {
                config.setPassword(properties.getPassword());
            }

            if (properties.getTimeout() != null) {
                config.setTimeout(Long.valueOf(properties.getTimeout().getSeconds() * 1000L).intValue());
            }

            if (!StringUtils.isEmpty(properties.getClientName())) {
                config.setClientName(properties.getClientName());
            }

        }
    }
}

  @Autowired
    RedissonClient redisson;

    private static final String LOCK_KEY_PREFIX = "lock_startPush_";

    protected String getLockKey(String courseId) {
        return LOCK_KEY_PREFIX + courseId;
    }

 public void startFromFirst(@RequestBody Course course) {
        try {
            ContentGrain contentGrainFirst = contentGrainList.findFirstContentGrain(course);
            this.setContentGrain(contentGrainFirst);
            contentGrainFirst.setContentGrainController(this);

            // 获取锁 固定的key
            RLock redissonLock = redisson.getLock(getLockKey(String.valueOf(course.getId())));
            // 如果其他线程在使用锁,提示用户课程正在同步中!
            if (redissonLock.isLocked()) {
                log.error("课程在在被锁住");
            }
            try {
                redissonLock.lock();
//这一句是要锁住的语句
                contentGrainFirst.start();

            } catch (Exception e) {
                log.error("lock 失败", e);
            } finally {
                // 删除key
                redissonLock.unlock();
            }

        } catch (Exception e) {
            log.error("保存颗粒error", e);
        }
    }

总结

并发是指在一个时间段内同时处理多个任务的能力。它强调任务的独立性和相互之间的交替执行。在并发模型中,多个任务交替执行,并且它们可以通过切换上下文(Context Switching)来共享系统资源。这种切换可以是基于时间片轮转(Time-slicing)的或者是基于事件触发的。并发常用于提高系统的响应能力、资源利用率和用户体验。

并行是指同时执行多个任务的能力。它强调任务的同时性和真正的并行执行。在并行计算中,多个任务同时进行,每个任务可以独立地在不同的处理单元(如多核处理器、分布式系统等)上执行。并行通常用于加速任务的处理速度,提高计算能力和处理大规模数据。

简而言之,"并发"关注的是多个任务如何交替执行,而"并行"关注的是多个任务如何同时执行。并发主要强调任务的独立性和资源共享,而并行主要强调任务的同时性和加速处理。

需要注意的是,并发和并行并不是互斥的概念。在某些情况下,可以同时使用并发和并行来提高系统的效率和性能。例如,在一个多核处理器上,可以通过并发执行多个任务,同时利用并行处理来加速每个任务的处理过程。
在这里插入图片描述

解决并发问题的其他方式:
互斥锁(Mutex):使用互斥锁来确保共享资源在同一时间只能被一个线程访问。当一个线程获得了互斥锁,其他线程需要等待该线程释放锁才能继续执行。这种方式可以避免多个线程同时对共享资源进行写操作导致的数据不一致性。

信号量(Semaphore):使用信号量来控制对共享资源的访问。信号量可以用来表示资源的可用数量,当资源被占用时,其他线程可以等待信号量变为可用再继续执行。通过适当设置信号量的值,可以控制并发访问共享资源的数量。

读写锁(Read-Write Lock):读写锁是一种特殊的锁,允许多个线程同时读取共享资源,但只允许一个线程进行写操作。这样可以提高读取性能,同时确保写操作的独占性。

条件变量(Condition Variable):条件变量用于线程间的通信和同步。一个线程可以等待某个条件变量满足特定条件,而其他线程可以通过触发条件变量来通知等待的线程继续执行。

并发数据结构:使用特定的并发数据结构可以避免共享资源的竞争。例如,使用并发队列(Concurrent Queue)可以实现多个线程之间的安全消息传递。

任务调度和协作:使用合适的任务调度和协作机制,将任务划分为适当的子任务,并进行调度和协调,以提高并发执行的效率和性能。例如,使用线程池(Thread Pool)来管理和复用线程,或者使用并发框架(如Java中的Executor框架)来简化并发编程。

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

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

相关文章

吉他如何实现内录or通过转接头和简易声卡连接电脑没有声音怎么办

目录 效果器or智能音箱 电吉他和效果器的连接 效果器和耳机or音箱连接 内录方法 为什么用6.5mm&#xff08;入&#xff09;转3.5mm&#xff08;出&#xff09;转接头内录无声音 整体连接图示 这篇文章我会以通俗的语言为初学者描述如何让电吉他“燃起来”&#xff0c;效果…

【每日算法 数据结构(C++)】—— 03 | 合并两个有序数组(解题思路、流程图、代码片段)

文章目录 01 | &#x1f451; 题目描述02 | &#x1f50b; 解题思路03 | &#x1f9e2; 代码片段 An inch of time is an inch of gold, but you can’t buy that inch of time with an inch of gold. An inch of time is an inch of gold, but you cant buy that inch of time…

mysql——存储过程

目录 存储过程存储过程的优点创建存储过程调用存储过程查看存储过程查看存储过程的详细信息查看存储过程的属性 存储过程的参数删除存储过程存储过程控制语句 存储过程 存储过程是一组为了完成特定功能的SQL语句集合存储过程在使用过程中是将常用或者复杂的工作预先使用SQL语句…

【Openvino02】openvino2022.1加速卡Movidius Myriad测试以及问题解决

接上一篇&#xff0c;上一篇我们已经把环境什么的都安装好了&#xff0c;本篇主要做一下各种模型测试&#xff0c;模型下载验证等&#xff1b;背景这里就不多说了&#xff0c;上篇已经介绍过了。 一、安装开发验证环境 1.安装所需包 这里建议用清华和豆瓣的镜像相互替换&…

4K 免费的图片素材去哪里找?这6个网站告诉你答案。

推荐几个免费高清图片素材库&#xff0c;今后再也不用为找图片而发愁了&#xff0c;建议收藏起来~ 菜鸟图库 https://www.sucai999.com/pic.html#?vNTYxMjky ​ 网站主要是为新手设计师提供免费素材的&#xff0c;素材的质量都很高&#xff0c;类别也很多&#xff0c;像平面…

C++primer(第五版)第一章(开始)

面对八百多页的C圣经《Cprimer》我陷入了沉思。最近终于下定决心把它啃下来&#xff0c;现在打算记录每章的关键点&#xff0c;一是为了巩固知识&#xff0c;二是以后要复习什么的也不用再碰那本砖头。 1.1编写一个简单的C程序 书中给了几行代码: int main() {return 0; } …

Crypto__1

编码 莫斯密码 Base编码 常见的Base64 Base32 Base16 Base64 将通过ASCLL码改成二进制数 因为64是2的6次方 所以将原文转换的二进制数分别取六位&#xff0c;不足的补0 每次取出6bit&#xff0c;按照其值选择 ABBCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123…

大模型部署实战(四)——ChatGLM2-6B

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Linux内核开发有用的工具网站

Linux内核官方相关 Linux内核主线源码 https://github.com/torvalds/linux 最新内核说明文档 https://docs.kernel.org/index.html Linux内核模块相关 内核配置与驱动查询 https://www.kernelconfig.io/index.html 内核模块编程指南 https://sysprog21.github.io/l…

mybatis模拟04

create SqlSession Class package com.wsd.core;/*** description: 执行sql* author: Mr.Wang* create: 2023-06-24 16:55**/ public class SqlSession { }SqlSessionFactory 中创建 openSqlSession method to get a sql session instance /*** description Get sql session in…

中国人民大学与加拿大女王大学金融硕士——在职读研让能力加速提升

不管你是初入职场的小白&#xff0c;还是久经沙场的元老&#xff0c;想要在职场有所作为&#xff0c;就不要忽略自我能力提升。决定一个人当前职场价值不是他拥有了什么&#xff0c;而是他将来能够创造什么。如果你只盯着工作&#xff0c;那么你的眼界和薪资将会被工作所决定&a…

LeetCode动态规划(一)之动规思想概述基础题目

文章目录 动态规划开撸——基础题目1. lc509 斐波那契数2. lc746 使用最小花费爬楼梯3. lc63 不同路径II4. lc343 整数拆分 动态规划 记住动归5部曲&#xff1a; 1.确定dp数组&#xff08;dp table&#xff09;以及下标的含义 2.确定递推公式 3.dp数组如何初始化 4.确定遍历…

MySQL——变量与游标

今天我们来一起学习MySQL中的变量&#xff08;系统变量与用户变量&#xff09;&#xff0c;以及什么是游标&#xff0c;游标如何使用&#xff1f; 1. 变量 在 MySQL 数据库的存储过程和函数中&#xff0c;可以使用变量来存储查询或计算的中间结果数据&#xff0c;或者输出最终…

2、动手学深度学习——线性神经网络:softmax回归的实现(从零实现+内置函数实现)

1、softmax回归 为了估计所有可能类别的条件概率&#xff0c;我们需要一个有多个输出的模型&#xff0c;每个类别对应一个输出。 为了解决线性模型的分类问题&#xff0c;我们需要和输出一样多的仿射函数&#xff08;affine function&#xff09;。 每个输出对应于它自己的仿射…

css基础知识八:如何实现两栏布局,右侧自适应?三栏布局中间自适应呢?

一、背景 在日常布局中&#xff0c;无论是两栏布局还是三栏布局&#xff0c;使用的频率都非常高 两栏布局 两栏布局实现效果就是将页面分割成左右宽度不等的两列&#xff0c;宽度较小的列设置为固定宽度&#xff0c;剩余宽度由另一列撑满&#xff0c; 比如 Ant Design 文档…

计算机网络 期末复习大总结 + 例题【全部复习】

计算机网络 期末复习大总结 例题 第 1 章 概 述TCP/IP 和 ARPANET端系统的通信方式互联网的核心部分 - 分组转发电路交换分组交换报文交换计算机网络的 性能指标 第二章 物理层信号 和 码元信道基带信号 和 调制常用的编码方式奈氏准则信噪比香农公式信道复用 第三章 数 据 链…

三阶魔方公式

1. 术语&#xff1a;上、下&#xff0c;左、右、前、后 2. 魔方实物图 上&#xff1a;黄色 下&#xff1a;白色 左&#xff1a;蓝色 右&#xff1a;绿色 前&#xff1a;红色 后&#xff1a;橙色 3. 转法 上加&#xff1a;上面顺时针转90 上减&#xff1a;下面逆时针转90 上2&…

Django项目之mysql数据库连接和表的创建

数据库连接 首先&#xff0c;确保我们已经生成了一个基本的Django项目文件&#xff0c;目录结构如下&#xff1a; 具体搭建流程参考链接&#xff1a;https://blog.csdn.net/David_house/article/details/131188889?spm1001.2014.3001.5502找到项目下的settings文件&#xff…

人工智能系统的业务架构

一、人工智能系统的业务架构&#xff1a;三大能力 二大业务方向 三大业务能力&#xff1a;交互能力、思考能力、服务能力两大应用方向&#xff1a;智能语音、机器视觉 ​ 首先在智能语音方面&#xff0c;人工智能三大业务能力对应的应用层面输出在交互能力里包括语音采集、语…

积分图估计法线方法

两种法线估计方法的比较 pcl::NormalEstimation和pcl::IntegralImageNormalEstimation是两种常见的法线估计方法&#xff0c;它们的区别主要在于计算法线的方式和适用场景。 pcl::NormalEstimation&#xff1a; 计算方式&#xff1a;基于K近邻搜索的方法&#xff0c;通过寻找点…