资讯专栏INFORMATION COLUMN

当我们在说“并发、多线程”,说的是什么?

sf_wangchong / 3362人阅读

摘要:兜底任务,处理数据不一致状态的任务。什么是多线程多线程是并发的一种重要形式。通过具体的多线程问题引出多线程编程中的关键点和对应的工具与知识点,轻松学会多线程编程。

这篇文章的目的并不是想教你如何造火箭(面试造火箭,工作拧螺丝),而是想通过对原理和应用案例的有限度剖析来协助你构建起并发的思维,并将操作系统的理论知识与工程实践结合起来,贯穿从学到会的全过程。当然,虽然我们是从实用角度出发,但具有实践意义的深层次知识点永远会是面试中的杀手锏,这可比只能口头造火箭的理论知识更吸引面试官。

本文适合谁:

希望能了解并发概念的初学者

需要理清并发概念与技术的工程师

对并发在工作中的应用与其底层实现原理感兴趣的读者

在这篇文章中,你将了解到并发与多线程相关的一系列概念,通过一些例子我们可以在不纠结于具体的技术细节的情况下形成对并发与多线程相关的各种概念的抽象理解。有了这些概念以后,我们再去学习具体的理论和技术细节就是手到擒来的事了。

什么是并发?

最近几年淘宝发展得如火如荼,涌现出了一大批白手起家的卖家。想象一下你是一个刚刚起步的小卖家,自己运营一个服装网店,每天都要自己打包发货。刚开始时生意一般,每天自己一个人一个小时就能干完。

随着生意的蓬勃发展,发货时间慢慢地从一个小时涨到了两个小时、四个小时,一次因为延迟发货导致被投诉之后,你终于觉得该招更多的人了。很快,两个小伙伴加入了你的事业,打包速度开始有了质的提高。这就是最基本的并发了,每个人都可以看成是一个线程,同样的工作量,干得人多了自然就快了。

所以并发就是通过多个执行器同时执行一个大任务来缩短执行时间、提高执行效率的方法。

数据竞争

但是好景不长,周末一盘货,你发现少了不少。这办公室里也没遭贼,怎么就会少货呢?细细一查快递单,你发现竟然有几单发重了。之后的几天你都细细留意了一下发货的过程,最后发现是因为每个人都会拿着一张发货清单去备货,如果有一些订单不小心打印重复了,就有可能会被不同的人重复发货。虽然数量不多,但是也很心痛啊。这个问题产生的原因就是因为每个人在备货之前拿到的订单状态(未发货)在实际备货时发生了变化(已由其他人发货)。这种对一个共享数据(订单的发货状态)本应独占的读取、检查、修改过程,如果发生了并发,这种情况就被称为数据竞争。而这个读取、检查、修改的过程就被称为临界区临界区指的就是一个存在数据竞争的代码片段。

数据竞争出现的根本原因是一个数据本来应该只能由一个执行器完整地执行读取、检查、修改过程,但是如果出现了并发,那么就没办法保证到了“修改”这一步时的数据还保持了“读取”时的值了。

确定原因后,有人想到了一个好办法,可以打印一张总的发货清单,这样所有人都必须以这个清单上的订单是否发货来确定是否要对订单进行备货并发货了。因为清单只有一份,所以每次只能由一个人来修改订单的发货状态。这种只能由一个执行器进行数据修改操作来避免发生数据竞争问题的做法就被称为互斥,也就是我们常说的了。

分布式并发概念 分布式

因为你管理得当,生意发展得很快,现在的办公室里已经堆不下所有衣服了。所以你又租了一个仓库来同样进行发货。两个地方都会进行发货,那么就可以把每一个仓库理解为一台独立的计算机,这样通过多台计算机完成同一任务的方式就可以被称为分布式,这样的一组计算机的集合就被称为集群

这时候之前用一张纸质的总发货清单的数据竞争解决方式就行不通了,所以我们需要把这张总发货清单放到云端,让大家可以通过网络进行编辑,但是每次只能一个人编辑。在这种情况下,我们可以把两个仓库各自看成一台计算机/进程,而每个仓库里的人就是这个进程中的线程。这样的话这张总发货清单就成为了一个分布式锁,因为它每次只能有一个人编辑,所以是一个互斥锁,或者简称为;而因为它可以被两个进程/计算机(仓库)共同使用,所以被称为是分布式锁

什么是进程/线程?

可以简单地将进程理解为我们电脑/手机上的一个应用,同一台手机上的每个App都是一个进程,同一个App在每个手机上也是一个进程。进程和进程之间可以理解为是两个仓库,互相之间物理隔离;而线程就是仓库里的每一个人,他们共享同一个办公空间。这里的办公空间就可以理解为操作系统中的虚拟内存空间,但是本文主要讨论并发相关的概念,就不继续展开了。

分布式数据不一致

因为生意比较好,所以所有人都很忙。有时候就会因为有一些人虽然在云端表格上已经勾上了一个订单,但是一忙就给忙忘了。其他人怕重复发货又不会再去处理已经勾上的订单了,因为这样导致的未发货订单让店铺被投诉了好多次,影响非常大。这种在并发过程中修改了数据状态但是没有完成后续执行的情况就会出现数据不一致,即订单已经被勾上,但实际并没有发货。

但是作为聪明的老板,你又想到了解决的方法。每隔一小时两个仓库就会各派一个人检查一下已经勾上的订单是否已经都打包完贴上快递单了。这种每隔一段时间就检查并处理遗漏的数据不一致订单的任务就被称为兜底任务。而通过兜底任务实现的在最后所有订单都会达到数据一致状态的情况就被称为最终一致性

优化方式

大家可能早就觉得前面介绍的总发货清单的方法太傻了,只要每个订单都只打印一张发货清单,由多带带一个人去负责分发清单就可以了,其他人只要处理好自己被分配到的订单就可以了。最后再加上一个兜底任务对订单的发货情况进行二次校验基本上就不会发生漏发或者重发的情况了。这种由一个执行器进行任务拆分,再由一组执行器进行处理,最后再由一个或一组执行器进行结果汇总的处理方式就是现在非常流行的map-reduce方法了。这种方法在大数据或者程序语言标准库里都有大量的应用,比如大数据领域赫赫有名的Hadoop和Java语言中的ForkJoinPool都使用了这种思想。

回顾

在这篇文章中,我们涉及到了以下的技术名词:

并发,通过多个执行器同时执行一个大任务来缩短执行时间、提高执行效率的方法。

数据竞争,对一个共享数据本应独占的读取、检查、修改过程发生了并发的情况。

临界区,存在数据竞争的代码片段。

互斥锁(也可以简称为“锁”),同一时间只能由一个执行器获取的实体,用于实现对临界区的互斥(只有一个)访问。

分布式,通过多台计算机完成同一任务的方式。

集群,一组完成同一任务的机器。

分布式锁,在不同机器/进程上提供互斥能力的锁。

数据不一致,一系列操作不具有原子性,一部分执行成功而另一部分没有,导致不同数据之间存在矛盾,例如订单已经是发货状态,但是实际没有发货。

兜底任务,处理数据不一致状态的任务。

最终一致性,通过兜底任务或其他方式保证数据不一致的情况最终会消失。

map-reduce,一种任务拆分-执行-再合并的任务执行方式,可以有效地利用多台机器、多核CPU的性能。

后记

因为并发的知识范围很大,而且对于一些抽象概念的传递必然会需要花费一些篇幅,所以这个主题将会包含一系列文章,主要覆盖以下主题:

什么是并发?

抛开冗长繁杂的技术点,直接理解并发相关的各种概念。

什么是多线程?

多线程是并发的一种重要形式。通过具体的多线程问题引出多线程编程中的关键点和对应的工具与知识点,轻松学会多线程编程。

常用工具中的并发实现

通过解析知名开源工具中的并发方案实现来深入理解并发编程实践。

有兴趣的读者可以继续关注后续的文章,在之后的文章中会有对并发编程、操作系统原语、硬件原语等等理论与实践知识的详细介绍与案例。

对数据库索引感兴趣的读者可以了解一下我之前的文章:

数据库索引是什么?新华字典来帮你 —— 理解

数据库索引融会贯通 —— 深入

20分钟数据库索引设计实战 —— 实战

数据库索引为什么用B+树实现? —— 扩展

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73536.html

相关文章

  • 这一次,让我们完全掌握Java线程(2/10)

    摘要:多线程不仅是后端开发面试中非常热门的一个问题,也是各种高级工具框架与分布式的核心基石。有兴趣的读者可以参考本系列的第一篇文章来了解一下并发相关的基本概念当我们在说并发多线程,说的是什么。 多线程不仅是Java后端开发面试中非常热门的一个问题,也是各种高级工具、框架与分布式的核心基石。但是这个领域相关的知识点涉及到了线程调度、线程同步,甚至在一些关键点上还涉及到了硬件原语、操作系统等更底...

    zgbgx 评论0 收藏0
  • 线程编程完全指南

    摘要:在这个范围广大的并发技术领域当中多线程编程可以说是基础和核心,大多数抽象并发问题的构思与解决都是基于多线程模型来进行的。一般来说,多线程程序会面临三类问题正确性问题效率问题死锁问题。 多线程编程或者说范围更大的并发编程是一种非常复杂且容易出错的编程方式,但是我们为什么还要冒着风险艰辛地学习各种多线程编程技术、解决各种并发问题呢? 因为并发是整个分布式集群的基础,通过分布式集群不仅可以大...

    mengera88 评论0 收藏0
  • Java线程池从使用到阅读源码(3/10)

    摘要:最后,我们会通过对源代码的剖析深入了解线程池的运行过程和具体设计,真正达到知其然而知其所以然的水平。创建线程池既然线程池是一个类,那么最直接的使用方法一定是一个类的对象,例如。单线程线程池单线程线程 我们一般不会选择直接使用线程类Thread进行多线程编程,而是使用更方便的线程池来进行任务的调度和管理。线程池就像共享单车,我们只要在我们有需要的时候去获取就可以了。甚至可以说线程池更棒,...

    468122151 评论0 收藏0
  • 线程中那些看不见的陷阱

    摘要:多线程编程就像一个沼泽,中间遍布各种各样的陷阱。但是在多线程编程或者说是并发编程中,有非常多的陷阱被埋在底层细节当中。线程池类中用于控制线程池状态和线程数的控制变量就是一个类型的字段。 多线程编程就像一个沼泽,中间遍布各种各样的陷阱。大多数开发者绝大部分时间都是在做上层应用的开发,并不需要过多地涉入底层细节。但是在多线程编程或者说是并发编程中,有非常多的陷阱被埋在底层细节当中。如果不知...

    phodal 评论0 收藏0
  • 对PHP-FPM和CGI,还有并发响应的理解

    摘要:官方对的解释是进程管理器。对并发访问的处理进程和线程从代码级别来讲不支持多线程操作,不能像等语言一样可以编写多线程代码。 关于本篇文章的部分纠正,请参考这篇文章:http://www.cppblog.com/woaido... 首先搞清楚php-fpm与cgi的关系 CGI CGI是一个web server与cgi程序(这里可以理解为是php解释器)之间进行数据传输的协议,保证了传递的...

    tianyu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<