1 开篇
并发编程是一个通用且早已成熟的领域。Java 只是根据自身情况做了实现罢了,当你理解或学习并发编程的时候,如果能够站在较高层面(站在管程这个理论模型的高度),系统且有体系地思考问题,那就会容易很多。
并发编程可以总结为三个核心问题:分工、同步、互斥。
- 分工指的是如何高效地拆解任务并分配给线程。
- 而同步指的是线程之间如何协作。
- 互斥则是保证同一时刻只允许一个线程访问共享资源。
Java SDK 并发包很大部分内容都是按照这三个维度组织的,例如 Fork/Join 框架就是一种分工模式,CountDownLatch 就是一种典型的同步方式,而可重入锁则是一种互斥手段。
2 如何学好并发编程
“跳出来,看全景”、“钻进去,看本质”。
2.1 跳出来,看全景
并发编程领域可以抽象成三个核心问题:分工、同步和互斥。
2.1.1 分工
Java SDK 并发包里的 Executor、Fork/Join、Future 本质上都是一种分工方法。除此之外,并发编程领域还总结了一些设计模式,基本上都是和分工方法相关的,例如生产者 - 消费者、Thread-Per-Message、Worker Thread 模式等都是用来指导你如何分工的。
2.1.2 同步
在并发编程领域里的同步,主要指的就是线程间的协作,本质上和现实生活中的协作没区别,不过是一个线程执行完了一个任务,如何通知执行后续任务的线程开工而已。
工作中遇到的线程协作问题,基本上都可以描述为这样的一个问题:当某个条件不满足时,线程需要等待,当某个条件满足时,线程需要被唤醒执行。
在 Java 并发编程领域,解决协作问题的核心技术是管程,上面提到的所有线程协作技术底层都是利用管程解决的。管程是一种解决并发问题的通用模型,除了能解决线程协作问题,还能解决下面我们将要介绍的互斥问题。可以这么说,管程是解决并发问题的万能钥匙。
2.1.3 互斥
互斥指的是同一时刻,只允许一个线程访问共享变量。
- 有锁。优化读多写少场景下,开可以用 ReadWriteLock、StampedLock 优化性能。
- 无锁。原理是不共享变量或者变量只允许读。这方面,Java 提供了 Thread Local 和 final 关键字,还有一种 Copy-on-write 的模式。
2.2 钻进去,看本质
工程上的解决方案,一定要有理论做基础。
比如,从 Java SDK 里面的条件变量 Condition,要想到 “它是从哪儿来的?是 Java 的特有概念,还是一个通用的编程概念?”,当知道它来自管程的时候,“管程被提出的背景和解决的问题是什么?” 这样一路探索下来,就会发现 Java 语言里的并发技术基本都是有理论基础的,并且这些理论在其他编程语言里也有类似的实现。所以技术的本质是背后的理论模型。
Java 语言里的并发技术基本都是有理论基础的,并且这些理论在其他编程语言里也有类似的实现。所以我认为,技术的本质是背后的理论模型。
对于某个具体的技术,要探索它背后的理论本质,理论的应用面更宽,一项优秀的理论往往在多个语言中都有体现,在多个不同领域都有应用。所以探求理论本质,既能加深对技术本身的理解,也能拓展知识深度和广度,这是个一举多得的方法。