并发编程之路

前奏

并发因为其在应用程序中举足轻重的地位而饱受关注,也因其复杂也让人一时难以摸清门道。引用<<菜根谭>>中的一句名句 原其初心,关其末路 ;我们理解了并发设计的初心后,就能够找准其脉络,纵使千丝万缕,也有迹可询。
应用程序中众多好的设计实现离不开底层硬件的支持,从Intel公司的摩尔定律中也让我们感叹科技发展之快。如今计算机基本都有多核的处理器,拥有能够同时处理多任务的能力。我们如何能够充分利用计算机帮我们更高效的完成更多的任务,在满足程序需求提升性能的同时节省更多的计算机资源。
能够实现并发的方式有几种:多线程、多进程和多协程。对于本文主要讨论的Java来说,是通过多线程的方式来实现并发机制的;而像C++,是在运行中调用Linux的系统API去“fork”出多个进程实现并发机制;像Go是在语言层面提供多协程。
例如如下几个场景:
1.web服务器通过线程池中的线程并发的处理请求任务
2.分布式实时计算框架中多线程并发处理实时任务
3.数据服务高可用架构中主从热备,主通过并发的将数据同步到多个从上
在以上的场景中我们看到了并发编程为应用程序带来的串行无法比拟的优势;但是在这些并发使用中,我们需要关注两个问题:
(1)有并发写(多个线程同时对公有数据有写的操作)的场景时,必然会有线程间数据一致性的问题,我们必须考虑线程安全问题并且以适当的机制来处理。例如常见的公有数据:
1.数据库服务器中的数据
2.分布式缓存中的数据
3.本地应用程序主存中的数据
(2)多线程协同,许多的异步场景都需要多线程间协同来完成。例如:
1.经典的生产者消费者模型
2.异步IO
3.常用的FutrueTask

java中并发编程常用机制

在JDK1.5之前
线程安全机制:
-synchronized:在java语法层面提供了synchronized关键字通过线程间互斥机制来实现线程间的同步,线程间只能以串行的方式来执行,这样来保证线程安全。synchronized是通过jvm中的moniterenter和moniterexist字节码指令实现的,moniterenter/moniterexist实际上是通过作用于主存中的变量的字节码指令lock/unlock实现的。
-线程隔离(TheadLocal):避免访问公有变量,每个线程都有自己独立的数据,这样来实现线程安全
线程间协同机制:
-在java语法层面提供了wait/notify API
-当然我们通过对主存中公有变量的访问也能实现线程间的协同

从JDK1.5开始
线程安全机制:
从JDK1.5开始,java语法层面提供了:
-原子操作(CAS):我们无法直接使用CAS相关的API来对我们的变量进行原子操作,不过jdk中为我们封装了相应的Atomic工具类来让我们使用;因为CAS原理是无锁的,是有赖于处理器对于原子指令集的支持将本来需要多步完成的指令用一步完成;我们需要注意避免ABA导致的问题。
-内存可见性(volatile):不是线程安全的,在使用过程中需要注意需要满足以下两个条件之一:
a.变量值的改变不依赖变量当前值
b.同时只有一个线程改变变量的值
-Lock: 几个实现类中的实现原理也应用了CAS、volatile、LockSupport机制
线程间协同机制:
-LockSupport中的park/unpark等API,unpark较notify一个很重要的特性是:unpark可以唤醒指定的线程,而notify只能唤醒随机的一个线程。

并发编程大师Doug Lea为我们贡献了一套强大的应用于多线程编程的工具,就是大家熟知的JUC包;大师深谙并发编程之道,在其中展示了深厚的底蕴,值得我们细细的品味和学习。

学习交流

写此文章的目的也是为了和大家更好的学习交流,有理解不当的地方欢迎大家指正,有什么好的建议欢迎大家给我留言,也欢迎大家关注我的博客,和微信公众号。

原创文章 2 获赞 3 访问量 1768

猜你喜欢

转载自blog.csdn.net/weixin_42208993/article/details/105744173