并发编程是一门很有意思的课题,需要涉及的知识也比基础和多元,比如操作系统、数据结构,JVM等,本文主要对其做一个概述,不做详论,后续的文章中会专门讲解每一个原理和相关的API
一:简介
编写正确的程序很难,而编写正确的并发程序难上加难。那么为什么我们还要去编写并发程序呢?要想充分发挥多核处理器的系统的强大计算能力,最简单的方式就是使用线程,随着处理器数量的持续增长,如何高效的使用并发正变得越来越重要。
早期的计算机没有操作系统,每次只能执行一个程序,这对于当时昂贵并且稀有的计算机资源来说也是一种浪费。操作系统的出现使得计算机能够同时运行多个程序,不同程序运行在不同的进程中,系统会为单独运行的进程分配各种资源,包括内存,标识文件的句柄,安全证书等等,需要的话还可进行一些粗粒度的通信,比如套接字,信号量,共享内存等。之所以要在计算机中加入操作系统来实现多个程序的同时执行,主要是基于以下原因:
资源利用率:等待一个任务完成的同时运行另一个程序执行任务显然能提高资源利用率。
公平性 : 通过粗粒度的CPU时间分片是不同用户和程序都能共享计算机资源。
便利性 :通过多个程序执行多个任务并在必要时通信,这比只编写一个程序计算所有任务更容易实现。
这些促使进程出现的原因同样也促使着线程的出现,因为线程更为轻量,灵活,线程允许在同一个进程内同时存在多个程序控制流,线程会共享进程可操作范围内的资源,例如内存和文件句柄,但是不同的是每个线程都有自己的程序计数器(Program Counter)、栈以及局部变量等,在同一个程序中的多个线程也可以同时被调度到多个CPU上执行,可以说线程是轻量级的进程。
并发编程主要的研究对象就是线程,在大多数的现代计算机操作系统中,都是以线程为基本的调度单位,而不是进程,如果没有明确的协同机制,那么线程之间是彼此独立执行的,由于线程共享进程拥有的内存,那么他们可能会同时操作某一块内存,这将造成不可预测的结果,此时我们需要一种比进程间通信更为细粒度的线程间通信机制,以正确的控制他们对数据的操作,尽最大可能避免出现哪些不可预测的行为!
二:并发编程有哪些风险
我们在编写并发程序的时候应当从以下问题开始思考程序的方方面面,抓住问题的本质进行编程。
安全性问题
线程安全性可能是非常复杂的,在没有充足同步的情况下,多个线程中的操作执行顺序是不可预测的,甚至会产生奇怪的结果。例如一种常见的并发安全问题是竞态条件 — Race Condition,由于线程的乱序执行导致在某种条件之下线程之间的数据操作产生了不正确的结果!
活跃性问题
安全性关注的点是永远不会发生糟糕的事情,而活跃性问题关注的点则是“某件正确的事情最终一定会发生”。当某个操作无法继续执行下去的时候就会出现活跃性问题,包括死锁,饥饿,活锁等等,后面我们会针对每个问题详细阐述。
性能问题
活跃性意味着某件正确的事情最终会发生,但是却不够好,因为通常我们希望正确的事情尽快发生。性能问题包括多方面,例如响应不灵敏,吞吐率过低,资源消耗过高或可伸缩性较低等等!
以上问题都会有产生的场景,原因,但是同大多数的并发错误一样,导致这些错误的原因是难以分析的,因为他们依赖于不同线程的执行时序!我们会在未来的文章中详细阐述发生这些问题的典型场景,原因,以及避免的办法,在开发过程中一定要提高对此类问题的重视程度。
三:Java并发编程知识体系
可以从四个方面建立一个完整的Java中并发编程知识体系。
基础知识
这部分关注很基础的概念的理解,包含了 , , , 。比如很常用的ConcurrentModificationException ,CopyOnWriteArrayList ,ConcurrentHashMap ,阻塞队列,同步工具类,CountDownLatch、CyclicBarrier和 Semaphore,FutureTask等等都是属于这部分的知识。
结构化并发应用程序
这部分会关注并发应用程序的结构组成方面,比如 ,, , 等方面。关键的常用API是Executor框架,比如Callable,Future,中断,ThreadPoolExcutor等关键类和概念。
活跃性、性能与测试
这部分关注活跃性问题和性能问题的方方面面,以及细节部分的内容,还会关注测试并发程序的问题。在这部分应该掌握死锁、活锁、饥饿、性能与可伸缩性,减少锁的竞争,垃圾回收机制等等。
高级主题
高级主题会从显式锁,构建自己的同步工具,接触操作系统的原子概念,CAS,ABA问题以及内存模型上呈现Java并发编程的高级主题。常用的API有Lock ,ReentrantLock,添加队列ReentrantLock.Condition,Synchronizer,AQS(AbstractQueuedSysnchronizer),ReentrantReadWriteLock,Semphore和CountDownLatch等等高级API,吃透这部分,那么并发编程这座大山基本就快翻篇了。
吾生亦有涯,而知亦无涯。关注公众号,一起学习,一起分享!