前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发编程的三大核心问题 -《深入理解高并发编程》

并发编程的三大核心问题 -《深入理解高并发编程》

作者头像
小傅哥
发布2022-07-18 11:59:00
3300
发布2022-07-18 11:59:00
举报
文章被收录于专栏:CodeGuide | 程序员编码指南

作者:小傅哥 博客:https://bugstack.cn

❝沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞

  • 一、分工问题
    • 1. 类比现实案例
    • 2. 并发编程中的分工
  • 二、同步问题
    • 1. 类比现实案例
    • 2. 并发编程中的同步
  • 三、互斥问题
    • 1. 类比现实案例
    • 2. 并发编程中的互斥
  • 四、送书五本
    • 活动说明
    • 五折优惠

并发编程的三大核心问题

并发编程并不是一项孤立存在的技术,也不是脱离现实生活场景而提出的一项技术。

相反,并发编程是一项综合性的技术,同时,它与现实生活中 的场景有着紧密的联系。

并发编程有三大核心问题

  • 分工问题
  • 同步问题
  • 互斥问题

本文就对这三大核心问题进行简单的介绍。

一、分工问题

关于分工,比较官方的解释是:一个比较大的任务被拆分成多个大小合适的任务,这些大小合适的任务被交给合适的线程去执行。

分工强调的是执行的性能。

1. 类比现实案例

可以类比现实生活中的场景来理解分工,例如,如果你是一家上市公司的 CEO,那么,你的主要工作就是规划公司的战略方向和管理好公司。就如何管理好公司而言,涉及的任务就比较多了。

这里,可以将管理好公司看作一个很大的任务,这个很大的任务可以包括人员招聘与管理、 产品设计、产品开发、产品运营、产品推广、税务统计和计算等。如果将这些工作任务都交给 CEO一个人去做,那么估计 CEO 会被累趴下的。CEO一人做完公司所有日常工作如图1所示。

图1 CEO 一人做完公司所有日常工作

如图1 所示,公司 CEO 一个人做完公司所有日常工作是一种非常不可取的方式,这将导致公司无法正常经营,那么应该如何做呢?

有一种很好的方式是分解公司的日常工作,将人员招聘与管理工作交给人力资源部,将产 品设计工作交给设计部,将产品开发工作交给研发部,将产品运营和产品推广工作分别交给运 营部和市场部,将公司的税务统计和计算工作交给财务部。

这样,CEO 的重点工作就变成了及时了解各部门的工作情况,统筹并协调各部门的工作, 并思考如何规划公司的战略。

公司分工后的日常工作如图2所示。

图2 公司分工后的日常工作

将公司的日常工作分工后,可以发现,各部门之间的工作是可以并行推进的。例如,在人力资源部进行员工的绩效考核时,设计部和研发部正在设计和开发公司的产品,与此同时,公司的运营人员正在和设计人员与研发人员沟通如何更好地完善公司的产品,而市场部正在加大力度宣传和推广公司的产品,财务部正在统计和计算公司的各种财务报表等。一切都是那么有条不紊。

所以,在现实生活中,安排合适的人去做合适的事情是非常重要的。映射到并发编程领域 也是同样的道理。

2. 并发编程中的分工

在并发编程中,同样需要将一个大的任务拆分成若干比较小的任务,并将这些小任务交给 不同的线程去执行,如图3所示。

图3 将一个大的任务拆分成若干比较小的任务

在并发编程中,由于多个线程可以并发执行,所以在一定程度上能够提高任务的执行效率。

在并发编程领域,还需要注意一个问题就是:将任务分给合适的线程去做。也就是说,该由主线程执行的任务不要交给子线程去做,否则,是解决不了问题的。

这就好比一家公司的 CEO 将规划公司未来的工作交给一位产品开发人员一样,不仅不能规划好公司的未来,甚至会与公司的价值观背道而驰。

在 Java 中,线程池、Fork/Join 框架和 Future 接口都是实现分工的方式。在多线程设计模式中,Guarded Suspension 模式、Thread-Per-Message 模式、生产者—消费者模式、两阶段终止模式、Worker-Thread 模式和 Balking 模式都是分工问题的实现方式。

二、同步问题

在并发编程中,同步指一个线程执行完自己的任务后,以何种方式来通知其他的线程继续执行任务,也可以将其理解为线程之间的协作,同步强调的是执行的性能。

1. 类比现实案例

可以在现实生活中找到与并发编程中的同步问题相似的案例。

例如,张三、李四和王五共同开发一个项目,张三是一名前端开发人员,他需要等待李四的开发接口任务完成再开始渲染 页面,而李四又需要等待王五的服务开发工作完成再写接口。

也就是说,任务之间是存在依赖关系的,前面的任务完成后,才能执行后面的任务。

在现实生活中,这种任务的同步,更多的是靠人与人之间的交流和沟通来实现的。例如,王五的服务开发任务完成了,告诉李四,李四马上开始执行开发接口任务。等李四的接口开发完成后,再告诉张三,张三马上调用李四开发的接口将返回的数据渲染到页面上。现实生活中 的同步模型如图4所示。

图4 现实生活中的同步模型

由图4可以看出,在现实生活中,张三、李四和王五的任务之间是有依赖关系的,张三渲染页面的任务依赖李四开发接口的任务完成,李四开发接口的任务依赖王五开发服务的任务完成。

2. 并发编程中的同步

在并发编程领域,同步机制指一个线程的任务执行完成后,通知其他线程继续执行任务的方式,并发编程同步简易模型如图5所示。

图5 并发编程同步简易模型

由图5可以看出,在并发编程中,多个线程之间的任务是有依赖关系的。

线程 A 需要阻塞等待线程 B 执行完任务才能开始执行任务,线程 B 需要阻塞等待线程 C 执行完任务才能开始执行任务。线程 C 执行完任务会唤醒线程 B 继续执行任务,线程 B 执行完任务会唤醒线程 A 继续执行任务。

这种线程之间的同步机制,可以使用如下的 if 伪代码来表示。

代码语言:javascript
复制
if(依赖的任务完成){
  执行当前任务 
}else{
  继续等待依赖任务的执行 
} 

上述 if 伪代码所代表的含义是:当依赖的任务完成时,执行当前任务,否则,继续等待依 赖任务的执行。

在实际场景中,往往需要及时判断出依赖的任务是否已经完成,这时就可以使用 while 循 环来代替 if 判断, while 伪代码如下。

代码语言:javascript
复制
while(依赖的任务未完成){
  继续等待依赖任务的执行 
}  
执行当前任务

上述 while 伪代码所代表的含义是:如果依赖的任务未完成,则一直等待,直到依赖的任务完成,才执行当前任务。

在并发编程领域,同步机制有一个非常经典的模型——生产者—消费者模型。如果队列已满,则生产者线程需要等待,如果队列不满,则需要唤醒生产者线程;如果队列为空,则消费者线程需要等待,如果队列不为空,则需要唤醒消费者。

可以使用下面的伪代码来表示生产者—消费者模型。

生产者伪代码

代码语言:javascript
复制
while(队列已满){
  生产者线程等待 
}  
唤醒生产者

消费者伪代码

代码语言:javascript
复制
while(队列为空){
  消费者等待 
}  
唤醒消费者

在Java 中,Semaphore、Lock、synchronized.、CountDownLatch、CyclicBarrier、Exchanger 和 Phaser 等工具类或框架实现了同步机制。

三、互斥问题

在并发编程中,互斥问题一般指在同一时刻只允许一个线程访问临界区的共享资源。互斥强调的是多个线程执行任务时的正确性。

1. 类比现实案例

互斥问题在现实中的一个典型场景就是交叉路口的多辆车汇入一个单行道,如图6所示。

图6 交叉路口的多辆车汇入一个单行道

从图6 可以看出,当多辆车经过交叉路口汇入同一个单行道时,由于单行道的入口只能容纳一辆车通过,所以其他的车辆需要等待前面的车辆通过单行道入口后,再依次有序通过单行道入口。这就是现实生活中的互斥场景。

2. 并发编程中的互斥

在并发编程中,分工和同步强调的是任务的执行性能,而互斥强调的则是执行任务的正确性,也就是线程的安全问题。

如果在并发编程中,多个线程同时进入临界区访问同一个共享变量,则可能产生线程安全问题,这是由线程的原子性、可见性和有序性问题导致的。

而在并发编程中解决原子性、可见性和有序性问题的核心方案就是线程之间的互斥。

例如,可以使用JVM中提供的synchronized锁来实现多个线程之间的互斥,使用synchronized锁的伪代码如下。

修饰方法

代码语言:javascript
复制
public synchronized void methodName(){
   //省略具体方法 
}

修饰代码块

代码语言:javascript
复制
public void methodName(){
   synchronized(this){
      //省略具体方法
   }
} 

public void methodName(){
   synchronized(obj){
      //省略具体方法  
   }  
} 

public void methodName(){
   synchronized(ClassName.class){
      //省略具体方法
   }  
}

修饰静态方法

代码语言:javascript
复制
public synchronized static void staticMethodName(){
   //省略具体方法 
}

除了 synchronized 锁,Java 还提供了 ThreadLocal、CAS、原子类和以CopyOnWrite 开头的并发容器类、Lock 锁及读/写锁等,它们都实现了线程的互斥机制。

本文节选自《深入理解高并发编程:核心原理与案例实战》,主要介绍了并发编程中的三大核心问题:分工、同步和互斥,并列举了现实生活中的场景进行类比,以便读者理解这三大核心问题。欢迎阅读此书了解更多关于并发编程的内容。

四、送书五本

赠送5本《深入理解高并发编程:核心原理与案例实战》,规则:对此文章进行留言并转发朋友圈,邀请伙伴给自己的留言点赞,排名1、2、3、4、5的五名粉丝伙伴,每人一本图书。

活动说明

  • 时间范围:2022-06-29 07:55:00 - 2022-06-30 19:00:00
  • 公布时间:2022年06月30日,星期四,在此文留言处置顶公布中奖消息

- END -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 bugstack虫洞栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📷
  • 一、分工问题
    • 1. 类比现实案例
      • 2. 并发编程中的分工
      • 二、同步问题
        • 1. 类比现实案例
          • 2. 并发编程中的同步
          • 三、互斥问题
            • 1. 类比现实案例
              • 2. 并发编程中的互斥
              • 四、送书五本
                • 活动说明
                  • - END -
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档