编程范式巡礼第二季 并发那些事

继续上周的编程范式话题,今天想聊一下并发范式。

并发也算一种范式?

真正的并发式编程,绝不只是调用线程API或使用synchronized、lock之类的关键字那么简单。从宏观的架构设计,到微观的数据结构、流程控制乃至算法,相比通常的串行式编程都可能发生变化。毫不夸张的说,是又一场思想和技术上革命。

在日常开发中,并发编程难度是比较高的,属于高级程序员才能掌握的内容。其难点在哪里,我们日常习惯的是线性思维,这与并发编程的多维世界观是不同的,提升思考的维度无疑是艰难。但还好,在大神们的努力下,已逐渐化繁为简,这也是并发范式带来的力量。在并发领域有许多的模型,让我们来巡礼一下。

模型1:线程与锁

并发编程以资源共享和竞争为主线。这意味着程序设计将围绕进程的划分与调度、进程之间的通信与同步等来展开。合理的并发式设计需要诸多方面的权衡考量。

线程是对底层硬件过程的形式化,是并发编程的核心。不同的线程各自独立运行,有如一个个的平行宇宙。但是,并发编程并不仅仅串行化编程的叠加,主要的差异在于,线程之间存在共享和竞争。共享资源会带来哪些问题呢?

  • 问题1:脏读

当线程1对共享数据进行修改时,线程2有可能会读到处于中间状态的数据,这个问题称之为脏读。

解决的思路比较简单,就是让线程1仅提交最终修改结果,在修改过程中产生并使用快照数据。这种类似影分身的技术称之为MVCC(Multi-Version Concurrency Control)。

  • 问题2:丢失更新

当线程1对数据进行修改时,如果线程2同时修改,由于采用了MVCC,双方各自无法看到,那最终提交时,很可能会造成其中一个线程结果与预期不一致,这个问题称为丢失更新。
其解决方法是加锁,在修改前进行加锁,一旦占用,则第二个线程无法获取。

脏读和丢失更新是需要同时考虑的,所以标准的多线程处理是同时使用到了MVCC和锁这两个技术。

  • 问题3:幻读

在已解决了丢失更新和脏读的情况下,下面要考虑多次读取的情况。如下图所示,线程1对数据集进行了多次读取,但是部分数据在线程2中进行了更新,这时候出现了线程1在没有任何作为的情况下,两次读取不一致的情况!!!这个问题称为幻读。

解决幻读的方法是扩大数据的锁范围,不仅仅是更改过的记录,所有读取的记录都要加锁。

  • 综述

这就是目前我们最主流的并发与锁的实现思路方法,有非常广泛的使用。不知道大家读完这段的感觉怎么,我看的时候,第一个感觉是复杂,真的非常的烧脑,由于大量概念的堆积对于初学者来说非常不友好;第二个感觉是矛盾,按照最终幻读的解决方案,实际上就是放弃了程序间的并行,绕了一圈,又回到了原点。正因为如此,目前主流的数据库,实际上默认都是放弃对于幻读问题解决的,这也是开发上的一大坑。

综合的来看,这种解决方式学习成本很高,而且还没能解决全部的问题,并不能让人满意。有没有更好点的方法,让我们继续。

模型2:函数式编程与Lambda架构

传统并发模型中,最令人纠结的无疑就是共享数据访问这块了。若不爽,就另辟蹊径。我们能不能不对共享数据进行写入呢?有什么样的程序是只读不写的呢,大神们已经找到了答案,就是上周介绍的函数式编程。

首先想说明的事,纯函数式的编程功能上并不完备,有非常多的缺陷,但其有一个天然的适用场景,就是数学运算分析,也就是我们现在时常挂在嘴边的大数据计算。
由于抛弃掉了共享状态,其代码的健壮性和扩展性得到了大大的增强,只要有足够的计算资源就可以处理无限大的数据。

函数式编程思维比较数学化,难度是比较高的,在此基础上,诞生了Lambda框架,是对应用模式的固化,有助于降低学习成本和大范围推广。Lambda框架既使用了可以进行大规模批处理的MapReduce技术,也使用了可以快速处理数据并及时反馈的流处理技术,这样的混搭能够为大数据问题提供扩展性、响应性和容错性都能优秀的解决方案。

Lambda架构也可以这样来描述:在该架构中,被读取的数据是不可变的,在并行处理过程中数据会依次进入批处理系统(batch system)与流处理系统。从逻辑上看,传输过程发生了两次,一次是在批处理中,一次是在流处理中。在查询时,当这两者都返回结果后,才算是完成一次完整的查询。

模型3:Actor

函数式编程模型的应用使得并发编程的应用踏入了工业级,带动了大数据的热潮。但是其解决思路是抛弃了可变状态,服务是有损的。对于必须提供无损服务的场景该如何进行改进呢。

从最一开始线程与锁的模型中,我们可以看到串行化是最重的解决方案,但是为了串行化,我们需要MVCC、锁等一系列的工具,比较复杂,Actor模型就是用来简化此类操作的。

Actor模型中抽象出了两个概念Actor和Mailbox,Actor就是指代共享数据,Mailbox管理数据的操作。对于每个Actor的操作,要通过mailbox来进行,在mailbox端实现了队列的控制,从而实现了序列化的效果。

Actor模型会带来一些额外的好处:

  1. 用Actor来定义共享数据,边界非常清晰,实现了与主线代码的解耦,最大化减少了序列化的影响,可以有效提升性能。
  2. Actor中引入了消息的概念,是位置透明的,天然支持了分布式的部署。
  3. 在概念清晰之后,代码得到了简化,下面摘录一段Actor的代码,可以看到是封装了并发相关的技术细节,非常的简洁。
class Pong extends Actor {def act() {var pongCount = 0while (true) {receive {case Ping =>if (pongCount % 1000 == 0)Console.println("Pong: ping " + pongCount)sender ! PongpongCount = pongCount + 1case Stop =>Console.println("Pong: stop")exit()}}}
}

模型4:原子变量

很多情况下我们需要一个高效的、线程安全的并发解决方案。高效意味着耗用资源要少,程序处理速度要快;线程安全也非常重要,这个在多线程下能保证数据的正确性。有一个解决方案是原子变量。

通常情况下,在Java里面,++i或者--i不是线程安全的,这里面有三个独立的操作:获得变量当前值,为该值+1/-1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作是“原子性”的。

下面是示例代码:

public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}
}

在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。而compareAndSet利用JNI来完成CPU指令的操作。

原子变量在一些对性能有极端要求的系统中(比如Jetty、Tomcat)有非常广泛的应用,是一种精益求精的体现,其在可靠性和性能方面表现很突出,但在易用性方面比较偏计算机思维,理解难度较大,并不够简洁,需要反复练习才能掌握。

小结

在今天的篇文章中,列举了并发范式的四个主流模型:线程与锁、函数式编程、Actor、原子变量。可以看到,每个模型都是在功能、性能和易用之间寻求了一种平衡,并没有一种模型在功能、性能和易用三方面同时达到最优,也就是说没有银弹。
这是我们面对并发问题时的困境,也是挑战,也正说明了并发并不是一个简单的线性问题,我们需要针对具体场景、具体问题进行分析,寻找最适合的解决方法,这也是开发人员需要养成的一种重要素养。

转载于:https://www.cnblogs.com/dt-zhw/p/6380022.html

小课堂week17 编程范式巡礼第二季 并发那些事相关推荐

  1. 小课堂week16 编程范式巡礼第一季 三大基石

    编程范式巡礼第一季 三大基石 最近迷上了一些哲史类书籍,回望过去.放眼未来,往往沉浸在其思维之美中无法自拔.计算机编程是一门非常年轻的学科,沉淀不足也是年轻的一个侧面,在编程领域,有足够思想深度的作品 ...

  2. Java面向对象小项目 慕课网Java入门第二季答答租车系统

    一.项目背景 编写一个控制台程序,要求实现如下功能: 1.展示所有可租车辆 2.选择车型,租车量 3.显示租车清单,包括:总载货量,总载客量,总金额等: 二.车的类别 客车:只能载客 货车:只能载货 ...

  3. Java面向对像小项目 慕课网Java入门第二季滴滴租车系统

    <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255) ...

  4. 视频教程-微信小游戏第二季:飞扬的肖恩/弹球打砖块-Cocos

    微信小游戏第二季:飞扬的肖恩/弹球打砖块 知识传播美丽,分享传递快乐.作者主要从事Java服务器端技术.前端与移动开发技术的研究和授课,已有10多年从业经验. IT老兵 ¥39.00 立即订阅 扫码下 ...

  5. 冒号课堂 编程范式与OOP思想

    上篇:编程范式与编程语言 第1课 开班导言 第2课 重要范式 第3课 常用范式 第4课 重温范式 第5课 语言小谈 第6课 语言简评 下篇:抽象机制与对象范式 第7课 抽象封装 第8课 抽象接口 第9 ...

  6. pyqt5 getsavefilename 默认文件名_经Jerry编程小课堂之python如何安装PyQt5和QT Designer...

    小伙伴们大家好,欢迎来到经Jerry编程小课堂,有没有很想我呢?嘿嘿嘿,我也很想你们啊!想死你们了,亲!嘤嘤嘤! 哈哈,话不多说,转入正题,今天我们聊一聊如何安装python的图形界面模块PyQt5以 ...

  7. 微信表情《程序员小黄第二季》上架啦!

    微信表情<程序员小黄第二季>上架啦! 用了三个软件(paint.net, gimp, ibisPaint)+ 三个硬件(iPad, ASUS laptop, MacBook Pro)完成的 ...

  8. 铁甲雄心机器人冠军_《铁甲雄心》第二季首播 优必选科技引领中国AI机器人进击之路...

    7月14日,由优必选科技独家冠名.创客星球与浙江卫视联合打造的机器人格斗竞技真人秀<铁甲雄心>第二季在浙江卫视晚间黄金档首播.优酷全网独播.节目热血燃情,向大众普及机器人文化和知识,其特殊 ...

  9. python print 换行_Python小课堂第21课:规整一下我们的输出之打印格式化与字符串...

    整齐的输出,不仅美观,还能方便我们更容易的定位问题的重点.所以我们有必要将我们的输出内容美化一下! 请点击右上角"关注"按钮关注我们,跟着木辛老师深入浅出的掌握输出格式化的方法吧! ...

  10. Lambda01 编程范式、lambda表达式与匿名内部类、函数式接口、lambda表达式的写法...

    1 编程范式 主要的编程范式有三种:命令式编程,声明式编程和函数式编程. 1.1 命令式编程 关注计算机执行的步骤,就是告诉计算机先做什么后做什么 1.2 声明式编程 表达程序的执行逻辑,就是告诉计算 ...

最新文章

  1. 为什么推荐 Java 程序员使用 Google Guava 编程
  2. Laravel 5.6 中文文档翻译完成,译者 60 人,耗时 10 天
  3. golang channel 管道 通道 信道 使用总结
  4. Kafka集群环境搭建
  5. ABAP Netweaver和git的快捷方式
  6. c语言中汉字属于什么类型_练字应该练什么之:汉字基本结构的类型
  7. mysql primary unique_MySQL中的INDEX,PRIMARY,UNIQUE,FULLTEXT之间的区别?
  8. Docker学习总结(66)—— Docker 的三大基石:Namespace、Cgroup 和 rootfs
  9. Java基础学习总结(155)——Java 日志框架怎么选?Logback Or Log4j2?
  10. ssm上传文件获取路径_ssm框架实现图片上传显示并保存地址到数据库(示例代码)...
  11. RabbitMQ:消息发送确认 与 消息接收确认(ACK)
  12. idea:properties in parent definition are prohibited警告去除
  13. 思步网6月底再次重磅出击:《CrystalBall User Manual 7.3(水晶球操作手册7.3)中英文对照版》
  14. 语音识别竞品分析报告
  15. 相机图像_基本知识储备
  16. 动态域名解析ipv6 群辉dnspod_群晖设置ipv6动态域名
  17. 持续发力Web技术 英特尔驱动万物智能互联
  18. 数字统计之统计页码数字出现的次数
  19. SSL-ZYC 牛车
  20. 台式计算机如何拆硬盘,机械硬盘怎么拆开?台式机3.5英寸机械硬盘拆卸方法图文教程...

热门文章

  1. OpenCV 3.0 高动态范围图像
  2. java公路中轴_技术帖 自行车中轴的种类 入门车友请细读
  3. 计算机发展变化英文作文60词,生活中的变化英文作文(精选4篇)
  4. A good article :csi cameras on the TX2 (the easy Way)
  5. Java 编写一个类Letter,要求该类创建对象p,并调用方法printLetter输出英文字母表。
  6. 审计工作存在的难点和问题_基层审计工作中存在的问题及建议
  7. Regular Expression简介
  8. 2019年制定的小目标
  9. 第三方支付相关知识结构
  10. 1843. 圆形牛棚