多线程编程彻底指南

多线程编程或者说范围更大的并发编程是一种很是复杂且容易出错的编程方式,可是咱们为何还要冒着风险艰辛地学习各类多线程编程技术、解决各类并发问题呢?java

由于并发是整个分布式集群的基础,经过分布式集群不只能够大大下降同等负载能力的价格,还能使总体可扩展到的负载能力上限大大提高。低廉的服务成本使互联网行业的创意井喷,任何一我的都有能力建立并维持一个服务于成百上千甚至数万人的应用服务;而极高的服务能力上限让无数业务的线上化成为了可能,大大拓宽了互联网技术与业务的边界。程序员

在这个范围广大的并发技术领域当中多线程编程能够说是基础和核心,大多数抽象并发问题的构思与解决都是基于多线程模型来进行的。并且这些并发问题的本质都是相同的,不论是线程并发、进程并发仍是服务器级别的并发都具备相似的特色、面临类似的问题,多线程编程正是咱们切入这个领域、学习并发问题解决方案的最好途径。因此,在如今的计算机行业中,多线程编程不只是Java程序员技术面试、进阶提升的重要知识领域,并且也是后端程序员敲开分布式系统实现大门的入场券。若是不能理解并发程序的特色与问题,那么就难以胜任分布式系统开发的工做。面试

这篇文章是一系列文章的总集篇,因此不须要读者有多线程相关的基础。文中会按照合理的顺序按部就班地介绍Java多线程编程的方方面面,由浅入深地讲解多线程编程的概念、使用、原理与实现。在每一部分都有对相关主题的简单介绍,再搭配上深刻讲解的文章连接,建议还不了解相关主题的读者能够深刻阅读连接中的文章来进行了解。但若是文章中间的一些内容你们已经很是熟悉了,那么能够略读而过,不用理会连接中的文章,彻底能够把这部份内容当作复习提纲来看。数据库

接下来,咱们会在这篇文章中系统地了解Java多线程编程知识体系,从最基础的基本概念、线程的使用开始讲起,一路覆盖多线程的正确性与运行效率相关议题,帮助你们从0到入门再到熟练掌握各类多线程编程技巧。在这以后,文章会渐趋复杂,咱们会深刻地讨论死锁的解决、事件驱动模型、同步机制的底层实现、线程池源代码解析等高级议题,帮助读者知其然更知其因此然,再也无惧于多线程相关的问题。编程

多线程基础

并发的概念

多线程首先是属于一种并发手段,因此咱们首先须要了解并发的基本概念。并发就是多个执行器同时执行不一样的任务,若是这些任务须要访问同一个数据,那么就会产生数据竞争。若是不能作好并发控制,那么数据竞争问题就有可能会致使程序最终的结果出现错误,也就是咱们常说的数据不一致。好比帐户A同时要扣三笔钱,那么若是三个线程同时执行扣款操做就有可能由于三个线程都用一开始的帐户余额减去一个值计算出三个结果并保存到帐户余额中,从而致使扣减结果之间的相互覆盖。除了多线程并发以外还有更重要的分布式并发主题,包括原子性、临界区、互斥、补偿、兜底任务等等专业术语,这些均可以在这篇不纠结于具体技术细节、只经过生活中的例子来说解并发概念的文章《当咱们在说“并发、多线程”,说的是什么?》中找到答案。后端

多线程编程基础

了解了并发的基本概念以后咱们就能够具体地在多线程编程领域中来了解具体的技术了。首先咱们先要了解,为何会须要多线程?多线程到底解决的是什么问题?而后,咱们就能够开始实际动手写真正的Java多线程编程代码了,一开始,咱们会直接使用Thread类来建立并运行线程。立刻咱们就碰到了多线程所带来的问题,咱们必须经过线程同步机制才能保证最后的输出结果正确。服务器

在《这一次,让咱们彻底掌握Java多线程》这篇文章中,咱们从多线程使用的场景开始讲起,只有弄明白了多线程到底能发挥什么样的做用咱们才能真正地在实践中使用好这门重要的技术。以后咱们会使用Thread来建立并运行线程,而后经过最基本的sychronized关键字来实现临界区的互斥访问,实现这一系列文章中的第一个正确的Java多线程程序。多线程

线程池的使用

但在实际的开发过程当中,咱们基本不会本身建立Thread类表明的线程而后管理它的执行。相反,咱们把任务交给一个线程池,而后让线程池本身管理任务的调度和线程的生命周期。线程池就像一个大管家,咱们只要给他设定好规则和预算,他就会自动帮咱们处理各类各样的任务。想要使用好线程池,那么你只须要看完《从0到1玩转线程池》这篇文章就够了!并发

多线程程序所面临的问题

多线程程序相比于单线程程序面临更多更复杂的问题,这就像掏蜂窝同样。咱们既想要蜂蜜的甘甜,可是又要时刻当心不要被蜇成了满脸包。通常来讲,多线程程序会面临三类问题:正确性问题、效率问题、死锁问题。分布式

正确性问题

正确性是程序的核心,若是一个程序产出的结果多是错误的,那么这个程序的价值必然大打折扣,甚至直接清零。咱们在以前的文章中使用synchronized关键字处理过多线程并发中的数据竞争问题。可是在实际的开发过程当中,咱们还会碰到更多各式各样的并发正确性问题。《多线程中那些看不见的陷阱》这篇文章中讲到了synchronized关键字、ReentrantLock显式锁、CAS操做、volatile关键字等一系列的线程同步工具,相信有了这些工具的保驾护航,咱们必定能够写出大量正确的多线程程序。

效率问题

虽然咱们能够利用线程同步工具箱中的十八般兵器写出正确的多线程程序,可是若是它执行得太慢甚至还比不上单线程程序的话那就得不偿失了。因此咱们不只要“对”,还要在“对”的前提下更“快”才行。在《多线程加速指南》这篇文章中,咱们能够利用CAS、ForkJoinPool、线程封闭、java.util.concurrent工具包等技术让咱们的多线程程序的速度提高10倍、100倍甚至是1000倍。

死锁问题

死锁问题相对来讲比较特殊,由于一旦出现死锁问题就会致使程序彻底没法继续执行。它既不会产生错误的结果,又由于程序会彻底中止因此已经不止是运行太慢的问题了。在各式各样的并发程序中都会遇到死锁问题,好比数据库、操做系统等等都会有这个问题。若是是咱们的我的电脑,那么死机以后重启就能够了,可是线上服务每每是不能中断的,这就须要咱们找到更多更好的解决方案来解决不一样状况下的死锁问题。相信读完这篇文章《解决死锁的100种方法》,你会对这个问题有更多的灵感。

多线程编程实战(实现一个阻塞队列)

讲完了这么多多线程相关的概念、技术与技巧,咱们也是时候下场练练手了。阻塞队列不只是多线程编程中的重要工具,并且还使用了互斥锁、条件变量、并发优化等等一系列重要的知识点来具体实现,这正是咱们练手的最佳素材。就让咱们跟随《从0到1实现本身的阻塞队列》的脚步,一块儿从0到1再到N,完成一个完整的JDK级别的阻塞队列实现。

高级主题

在看过多线程的基础知识、关键技术,最后又完成了一次练手之后,咱们就能够继续深刻多线程领域中更深奥的高级主题了。

线程池运行模型源码解析

在以前的文章中,咱们已经掌握了线程池的使用方法,虽然线程池是一个称职的管家,可是若是咱们不了解它的脾气就有可能在不自觉的时候越过了一些它的底线,最后被它给狠狠地甩在了地上。那么如今就让咱们经过《线程池运行模型源码全解析》来剖析线程池的运行模型,从源码角度了解线程池究竟是怎么运转的。

同步机制的底层实现

咱们已经使用过了这么多的线程同步机制,这些线程同步机制显得那么的神奇,帮助咱们躲开一个又一个的陷阱。那么这些这么厉害的东西究竟是怎么实现的呢?这时候就要请出咱们的幕后英雄AbstractQueuedSynchronizer(简称AQS)了。java.util.concurrent中的大多数线程同步类都是基于AQS实现的,好比经常使用的就有可重入互斥锁ReentrantLock、闭锁CountDownLatch、可重入读写锁ReentrantReadWriteLock、信号量Semaphore。在《同步机制的底层实现》中,咱们能够一探究竟,看看AQS是如何实现这么多风格迥异的线程同步机制的。

总结

到这里,咱们就完成了整个Java多线程知识体系之旅。在这个过程当中,咱们首先了解了并发的基本概念和Java多线程编程的基本方法,而后出现了线程池这个优秀的管家为咱们打理好了任务执行与线程调度的全部麻烦事。以后咱们系统地了解并解决了多线程中的三类主要问题:正确性问题、效率问题和死锁问题。在掌握了这么多Java多线程编程的知识与技巧以后,咱们就经过实现一个阻塞队列来了一次大练兵,不只能检验咱们的多线程编程技能,同时也加深了咱们对这些知识的理解。最后,咱们进入了多线程知识的深水区,经过JDK与Netty的成熟源代码研究了三个更底层的高级主题:事件驱动模型、线程池运行模型、同步机制的底层实现。