背景
最近在研究并发问题,解决并发问题有三种方式:多线程,线程池,和同步异步,今天就来讲解同步异步。
过程
明确概念
什么是cpu?
工厂是时刻在运行的,因此可以理解cpu时刻在运行。
什么cpu的核数?
假设把一个cpu比作一份电量的话,一份电量又只能满足一个车间运行,那么其他车间就得停止运行,所以一个cpu只能执行一个任务,必须等这个任务结束后才能分配电量给其他车间。
什么是进程?
把车间理解为应用程序(流水线生产),即进程,那么一份电量只能给一个车间,也就是一个cpu只能执行一个应用程序(进程)。
什么是线程?
车间的流水线需要多个工人才能完成任务,因此,把工人理解为线程,即一个进程可以有多个线程来完成计算任务,但一个车间又有许多房间(单个进程内的内存空间),那工人是可以共享这些房间资源,比如上厕所,吃饭的房间,因此可以理解为线程之间是可以共享进程内部的内存空间。
什么是互斥锁?
车间的房间资源是有限的,比如厕所只能排队上,必须别人用完之后才可以让下一个人用,但万一有人还在上厕所就强行进行厕所怎么办?加把锁即可,让他必须等着。作用是防止多个线程同时读取同一块内存区域。
什么是进程的调度?
以前是怎么实现一个cpu同时运行qq,微信,音乐的呢?实际上这是cpu通过时间片算法或者其他调度算法实现了交替运行,由于速度极快,人是感知不到的。
比如我开了三个软件,系统会给三个程序A、B、C开辟一个空间,在这空间里,给他们分配不同的运行时间,一个程序运行完后就会执行下一个,轮流着来,如果发现A程序这次时间不够,下次轮到他的时候就给他多分配一点时间。
所以实际上还是单任务的处理。
什么是并发?
进程的调度实际上就是实现了并发,即多任务同时或者伪同时的执行。相当于吃饭吃一半,停下来去接电话,接完再吃饭,只是过程很快,做到了像同时运行。
什么是并行?
工厂不可能只让一个车间运行,得同时让多个车间运行,但电量(cpu)不够怎么办,加多几个cpu,因此,并行只能在多核cpu下才能实现,所以并行就是多个进程同时进行。相当于我可以边吃饭边看电视。
什么是同步?
比如我上手机店买手机,服务员去仓库拿手机,要花一分钟,我等了一分钟后拿到手机才去买水喝。这个过程就是当程序遇到阻塞,需要等待的时候,程序会一直等待阻塞解除或者运行完后才进行下一步执行。
什么是异步?
比如我上手机店买手机,服务员去仓库拿手机,要花一分钟,我趁这一分钟才去买水喝,然后回来刚好拿到手机。这个过程就是当程序遇到阻塞,需要等待的时候,程序会先执行其他任务,完成任务后回来,刚好上个任务也已经完成,可以提高效率。
什么是异步编程?
异步编程是一种编程范式,用于处理并发性问题和提高程序的响应性。在传统的同步编程中,程序按照严格的顺序执行,每个任务必须等待上一个任务完成后才能执行。这种方式在面对I/O密集型操作(如网络请求、文件读写、数据库查询等)时,可能会导致阻塞,浪费了系统的资源和时间。
异步编程的核心思想是任务之间的解耦,使得某些任务可以在等待某些操作完成的同时,可以继续执行其它任务,而不必等待阻塞的操作完成。这样可以大大提高程序的并发性和响应性,使得程序在进行I/O操作时不会浪费时间,而是可以继续执行其它任务,充分利用系统资源。
并发编程的三大问题
分工问题
分工(Task Partitioning):分工是指将任务分解成多个子任务,并由多个线程并行执行这些子任务。每个线程独立执行自己的子任务,从而提高整体的并发性和执行效率。通过分工,可以将一个大任务拆分成多个小任务,并让多个线程并行地处理这些小任务,从而充分利用多核处理器的计算能力。
关于分工,就是一个比较大的任务被拆分成多个大小合适的任务,这些大小合适的任务被交给合适的线程去执行。分工注重的是执行的性能,现实生活场景如下:
如果你是一家上市公司的CEO,那么你的主要工作就是规划公司的战略方向和管理好公司,就管理好公司而言,涉及的任务就比较多了。
这时可以将管理好公司看作是一个比较大的任务,这个比较大的任务包括人员招聘与管理,产品设计、产品研发、产品运营、产品推广、税务和计算等。如果将这些任务都交给CEO一个人去做,那么就算CEO被累趴下就做不完的,工作任务示意图如下
若是公司CEO一个人做所有的工作任务是一种非常糟糕的方式,这将导致公司无法正常运营,那么该如何做呢?就是分工。
将人员招聘与管理交给人力资源部,将产品设计交给设计部,将产品研发交给研发部,将产品运营交给运营部,将产品推广交给市场部,将税务和计算交给财务部,这样,公司CEO的重点工作就变成了及时了解各部门的工作情况,统筹协调各部门的工作,并思考如何规划公司的战略发展,工作任务示例图如下
将管理好公司这个任务进行工作分工后,可以发现各部门之间的工作可以并行推进。如人力资源部在进行员工的绩效考核时,设计部和研发部正在设计和研发公司产品,与此同时,公司的运营人员正在和设计人员和研发人员沟通如何更好的完善公司的产品,而市场部正在加大力度宣传和推广公司产品,财务部正在统计各种财务报表,大家分工明确,工作稳步同时进行。
所以在现实生活中,安排合适的人去做合适的事情是非常重要的,在并发编程领域同样如此。
在并发编程中,需要将一个比较大的任务拆分成若干个比较小的任务,并将这些小任务交给不同的线程去执行,如下
在并发编程中,由于多个线程可以并发执行,所以在一定程度上能够提高任务的执行效率,提升性能。但注意该由主线程执行的任务不要交给子线程去执行,否则是解决不了问题的。如现实生活场景中CEO将规划公司未来的工作交给研发人员一样,不仅不能规划好公司未来,可能会与公司的价值背道而驰。
同步问题
同步(Synchronization):同步是指在线程之间建立合适的协调机制,确保多个线程在执行任务时能够按照一定的次序和规则进行协作,避免并发冲突和数据竞争。通过同步,可以保证多个线程之间的协作和交互,避免数据不一致和并发问题。
关于同步,就是指一个线程执行完自己的任务后,以何种方式来通知其他的线程继续执行任务,也可以理解为线程之间的协作。同步注重的是执行的性能,现实生活场景如下:
如果有一个项目由曹操、孙权和刘备共同开发,曹操是一名前端研发人员,需要等待孙权的接口任务完成后再渲染页面,而孙权又需要等待刘备的数据层服务开发完成后再写接口服务。
可见,任务之间是存在依赖关系的,前面的任务完成之后才能执行后面的任务。
在现实生活场景中,这种同步更多的是靠人与人之间的沟通来实现,任务同步示意图如下
由图可以看出,曹操、孙权和刘备的任务之间是有依赖关系的。
在并发编程中,同步机制指一个线程的任务执行完成后,通知其他线程继续执行任务的方式,如下
在多线程设计模式中,生产者消费者模型也是典型的同步实现机制。如果队列已满,则生产者需要等待,如果队列不满,则需要唤醒生产者线程;如果队列为空,则消费者需要等待,如果队列不为空,则需要唤醒消费者。
在多线程设计模式中,生产者消费者模型也是典型的同步实现机制。如果队列已满,则生产者需要等待,如果队列不满,则需要唤醒生产者线程;如果队列为空,则消费者需要等待,如果队列不为空,则需要唤醒消费者。
在java中,synchronized、Lock、Semaphore、CountDownLatch、CyclicBarrier等工具都是实现同步机制。
互斥问题
互斥(Mutual Exclusion):互斥是同步的一种重要机制,它指的是对共享资源的访问进行临界区控制,保证在同一时刻只有一个线程可以访问共享资源,其他线程需要等待。互斥机制可以避免多个线程同时对共享资源进行修改导致的数据不一致问题,同时也保证了对共享资源的安全访问。
关于互斥,就是指在同一时刻只允许一个线程访问临界区的共享资源。互斥注重的是多个线程执行任务时的正确性,现实生活场景如下:
如果人民公园只有一个单人的公共卫生间,众多游客都需要去卫生间,示意图如下
总结
在多线程编程中,分工和同步是两种重要的策略,通过合理地分工和同步,可以最大程度地发挥多线程的并发性和并行性,提高程序的执行效率和性能。然而,分工和同步也需要谨慎使用,避免出现死锁、饥饿等并发问题。因此,在多线程编程中,需要综合考虑业务逻辑和数据访问方式,合理选择合适的分工和同步策略,从而确保多线程程序的正确性和性能。