Java 并发编程实战
开篇词 | 你为什么需要学习并发编程文章目录开篇词 | 你为什么需要学习并发编程学习攻略 | 如何才能学好并发编程跳出来看全景1. 分工2. 同步3. 互斥钻进去看本质总结课后精彩留言其实并发编程可以总结为三个核心问题分工、同步和互斥。分工指的是如何高效地拆解任务并分配给线程。同步指的是线程之间如何协作。互斥指的是保证同一时刻只允许一个线程访问共享资源。学习攻略 | 如何才能学好并发编程并发编程并不是一门相对独立的学科而是一个综合学科。并发编程相关的概念和技术看上非常零散相关度也很低总给你一种这样的感觉我已经学习很多相关技术了可还是搞不定并发编程。那如何才能学习好并发编程呢其实很简单只要你能从两个方面突破一下就可以了。一个是“跳出来看全景”另一个是“钻进去看本质”。跳出来看全景我们先说“跳出来”。你应该也知道学习最忌讳的就是“盲人摸象”只看到局部而没有看到全局。所以你需要从一个个单一的知识和技术中“跳出来”高屋建瓴地看并发编程。当然这首要之事就是你建立起一张全景图。1. 分工所谓分工类似于现实中一个组织完成一个项目项目经理要拆分任务安排合适的成员去完成。在并发编程领域你就是项目经理线程就是项目组成员。任务分解和分工对于项目成败非常关键不过在并发领域里分工更重要它直接决定了并发程序的性能。Java SDK 并发包里的 Executor、Fork/Join、Future 本质上都是一种分工方法。除此之外并发编程领域还总结了一些设计模式基本上都是和分工方法相关的例如生产者 - 消费者、Thread-Per-Message、Worker Thread 模式等都是用来指导你如何分工的。2. 同步在并发编程领域里的同步主要指的就是线程间的协作本质上和现实生活中的协作没区别不过是一个线程执行完了一个任务如何通知执行后续任务的线程开工而已。协作一般是和分工相关的。Java SDK 并发包里的 Executor、Fork/Join、Future 本质上都是分工方法但同时也能解决线程协作的问题。例如用 Future 可以发起一个异步调用当主线程通过 get() 方法取结果时主线程就会等待当异步执行的结果返回时get() 方法就自动返回了。主线程和异步线程之间的协作Future 工具类已经帮我们解决了。除此之外Java SDK 里提供的 CountDownLatch、CyclicBarrier、Phaser、Exchanger 也都是用来解决线程协作问题的。不过还有很多场景是需要你自己来处理线程之间的协作的。工作中遇到的线程协作问题基本上都可以描述为这样的一个问题当某个条件不满足时线程需要等待当某个条件满足时线程需要被唤醒执行。例如在生产者 - 消费者模型里也有类似的描述“当队列满时生产者线程等待当队列不满时生产者线程需要被唤醒执行当队列空时消费者线程等待当队列不空时消费者线程需要被唤醒执行。”在 Java 并发编程领域解决协作问题的核心技术是管程上面提到的所有线程协作技术底层都是利用管程解决的。管程是一种解决并发问题的通用模型除了能解决线程协作问题还能解决下面我们将要介绍的互斥问题。可以这么说管程是解决并发问题的万能钥匙。3. 互斥分工、同步主要强调的是性能但并发程序里还有一部分是关于正确性的用专业术语叫“线程安全”。并发程序里当多个线程同时访问同一个共享变量的时候结果是不确定的。导致不确定的主要源头是缓存导致的可见性问题、线程切换带来的原子性问题、编译优化带来的有序性问题和。为了解决这三个问题Java 语言引入了内存模型内存模型提供了一系列的规则利用这些规则我们可以避免可见性问题、有序性问题但是还不足以完全解决线程安全问题。解决线程安全问题的核心方案还是互斥。所谓互斥指的是同一时刻只允许一个线程访问共享变量。实现互斥的核心技术就是锁Java 语言里 synchronized、SDK 里的各种 Lock 都能解决互斥问题。虽说锁解决了安全性问题但同时也带来了性能问题那如何保证安全性的同时又尽量提高性能呢可以分场景优化Java SDK 里提供的 ReadWriteLock、StampedLock 就可以优化读多写少场景下锁的性能。还可以使用无锁的数据结构例如 Java SDK 里提供的原子类都是基于无锁技术实现的。除此之外还有一些其他的方案原理是不共享变量或者变量只允许读。这方面Java 提供了 Thread Local 和 final 关键字还有一种 Copy-on-write 的模式。使用锁除了要注意性能问题外还需要注意死锁问题。跳出来看全景可以让你的知识成体系所学知识也融汇贯通起来由点成线由线及面画出自己的知识全景图。并发编程全景图之思维导图钻进去看本质但是光跳出来还不够还需要下一步就是在某个问题上钻进去深入理解找到本质。我属于理论派我认为工程上的解决方案一定要有理论做基础。所以在学习并发编程的过程中我都会探索它背后的理论是什么。比如当看到 Java SDK 里面的条件变量 Condition 的时候我会下意识地问“它是从哪儿来的是 Java 的特有概念还是一个通用的编程概念”当我知道它来自管程的时候我又会问“管程被提出的背景和解决的问题是什么”这样一路探索下来我发现 Java 语言里的并发技术基本都是有理论基础的并且这些理论在其他编程语言里也有类似的实现。所以我认为技术的本质是背后的理论模型。总结当初我学习 Java 并发编程的时候试图上来就看 Java SDK 的并发包但是很快就放弃了。原因是我觉得东西太多眼花缭乱的虽然借助网络上的技术文章感觉都看懂了但是很快就又忘了。实际应用的时候大脑也一片空白根本不知道从哪里下手有时候好不容易解决了个问题也不知道这个方案是不是合适的。我知道根本原因是我的并发知识还没有成体系。我想要让自己的知识成体系一定要挖掘 Java SDK 并发包背后的设计理念。Java SDK 并发包是并发大师 Doug Lea 设计的他一定不是随意设计的一定是深思熟虑的其背后是 Doug Lea 对并发问题的深刻认识。可惜这个设计的思想目前并没有相关的论文所以只能自己琢磨了。分工、同步和互斥的全景图是我对并发问题的个人总结不一定正确但是可以帮助我快速建立解决并发问题的思路梳理并发编程的知识加深认识。我将其分享给你希望对你也有用。课后精彩留言