【置顶】Java多线程
条评论引言
Java多线程和锁是分布式和并发编程的基础,深入理解Java多线程是我们提升内功的重要途径。我将试图由浅入深学习Java多线程编程,从如何使用入手,掌握如何使用之后再探求其中的内部原理,需要时也会对源码进行解读。
目录
以下为各个章节的目录,应用相关的我称之为基础篇,原理相关的我称之为中级篇,其中可能有部分比较复杂的,我称之为高级篇。以下内容多为原创,图是从参考资料中截取的。
多线程基础篇
以代码为例,展示最基本的应用场景和使用方法。简单说,就是怎么使用。
为什么需要多线程,多线程能够解决什么问题?多线程又带来哪些问题?如何拥有新的一个线程?
有了线程之后,如何管理?线程如何调度?如何创建线程池?线程池有哪些参数,能够控制哪些行为?
创建了多线程之后,多线程之间如何通信?如何使用synchronized和wait/notify实现线程同步?如何编写经典的生产者-消费者代码?
并行包java.util.concurrent如何使用?
ThreadLocal如何使用?
[多线程基础篇(6) ThreadLocal]
多线程中级篇
探求内部实现原理和复杂一些的用法。简单说,就是内部基本原理。
线程从创建到销毁,内部都要经过哪些状态?各个状态之间如何变化?线程状态迁移图是怎样的?
当启动线程后,如果取消、关闭和中断一个进行中的线程?
什么是JMM?什么是重排序?什么是内存屏障?volatile如何解决内存可见性与有序性问题?
多线程有哪些问题?什么是临界资源和竞争条件?什么是虚假唤醒?什么是死锁、嵌套死锁和重入死锁?什么是线程饥饿?
[多线程中级篇(4) 多线程同步问题]
并行包中
多线程高级篇
复杂原理,或者操作系统级别的实现原理。简单说,就是如果从头自己实现,需要怎么做。
线程底层是如何实现的?一个JAVA线程对应操作系统的什么?
实现多线程有哪些常见的模型?Java采用的是哪一种模型?并发和并行的区别?
[多线程高级篇(2) 线程模型]
为什么要学习多线程
并发是Java开发领域中经常被提及的问题,也是比较复杂的问题。提到并发就不得不提到多线程和锁,可以认为JVM的线程模型和加锁机制是Java并发的基础。实际工作中,我们可能不会经常去操作线程池,使用wait/notify进行线程间通信,所以对多线程感受并不强烈。这是因为我们通常都是在框架的基础上进行开发,框架帮我做了这些事情,虽然我们没有感觉到,但是从服务启动那一刻开始,Java多线程机制一直在运转。
多线程少被提及的另一个原因,我认为与现代系统都是分布式系统有关,或者说是多进程的分布式系统有关,更多被提到的是多进程之间如何通信,如果处理高并发的问题。线程间通信局限在一个JVM进程之内,所以多线程的处理机制并不能解决多进程的问题(例如:synchronized可以在一个JVM进程内加锁实现线程互斥的目的,但是当我们分布式部署时,一个服务有多个进程实例,synchronized就起不到相应的作用了)。
既然在分布式系统中多线程机制已经不能解决问题了,那我们为什么还需要去了解Java多线程的原理和应用呢?
- 首先,每个服务都是一个JVM进程,由多个线程组成,虽然框架帮我们封装了线程操作,把我们从复杂的多线程调度中解放处理,专注于业务逻辑;但是当出现问题或者需要调优时,了解底层的实现机制就显得必要;可以做一个这样的类比,平时我们也可以不关注JVM GC相关的内容,通常我们的Java应用可以很好的运行,但是当出现内存不足、内存泄漏等问题时,就需要我们了解GC的原理了。
- 其次,初期我们通常选用开源框架搭建业务服务;但是要明白框架或者中间件的出现通常是为了满足一个具体的需求而产生的,当任务完后进行抽象、剥离,开源后形成框架的;这就意味着所谓开源框架不是为你的业务需求而生的,很可能在某些方面不能满足、或者不能完美实现我们的需求;这个时候就需要我们在开源框架的基础上进行定制化修改(例如:阿里定制化MySQL),或者完全重新实现一个(例如:阿里借鉴Kafka原理实现RocketMQ),这个时候理解多线程机制就显得尤为重要了。
- 最后,多线程并发和多进程并发虽然有区别,但是也有很多类似之处;虽然具体的处理方法上可能差别较大,但是如果从思路和原理的角度来看,两者有非常多的相同点。例如,我们知道JDK1.5的并发包中提供了种类丰富的锁,那么我们在实现分布式锁的时候,就可以参考JDK中实现。还有,虽然多线程解决的是一个进程内的问题,但是和多进程一样都是并发操作引发的问题,即使解决方案不同,遇到的问题也是类似的。例如,多线程里面经常提到volatile,volatile解决的是什么问题呢?我们这里不讨论如何解决,只讨论问题是什么。我认为,本质上volatile解决的是由于缓存带来的与主内存不一致的问题。接下来开一下脑洞,多线程存在这个问题,多进程是否也有同样的问题呢?当然存在,如果我们把数据库看作主内存,那么在进程中缓存数据库中的数据就和线程的工作内存没有两样,当回写时就有可能出现问题。
当我们遇到问题时,类比是一种解决问题的思路。能够创新性的发明一种新的方法来解决问题的是大师,可遇而不求;能够找到类似问题的历史解决方案并设计出相应的解决方法,
通俗的说,如果你可以熟练使用Java多线程编程,那么你就至少是中级程序员了;如果还可以明白背后的原理,并引申有自己的思考,那么我认为你自称高级程序员不会有人质疑。
关于Java并发和多线程的资料有很多,网上一搜一大堆。接下来我会按照我的理解和思路进行一番串联。我发现很多书籍都是先讲原理,再讲应用,这和我们的实践是相悖的。通常,我们都是在不完全了解的情况下先使用,在使用过程中发现问题或者不清楚的地方再去看原理,这才是正常的思维。所以,我会先从应用开始:首先,我们要达到一个什么样的目的(目标);接下来,我们要如何做才能实现目标(应用),最后才是为什么要这么做,其背后深层次的原因是什么(原理)。
有时候想,如果现在让我重新回到大学去学习计算机组成原理、操作系统原理、编译原理等,应该有不同理解。
参考资料
http://www.cnblogs.com/skywang12345/p/3479949.html
https://blog.csdn.net/huzhigenlaohu/article/details/51627201
http://ifeve.com/non-blocking-algorithms/
笔记
为什么要有多线程,多线程解决哪些问题?现代计算机拥有多个处理器,每个处理器又拥有多个核心;多线程的目的就是为了提升性能,让多个任务可以同时运行。如果只有一个线程,那么只会用到一个处理器的一个核心,CPU资源将被浪费。
多线程带来好处的同时,也带来成本和开销。
实现多线程有很多中模型,Java采用的是哪一种?(高级)
并发和并行的区别?
多线程和多进程的区别?
如何拥有一个线程?怎么把多线程创建出来?
引入多线程以后,带来了哪些问题?临界资源/竞争条件/
哪些资源是临界资源,可能在多线程执行过程中出现问题(内存、栈、堆)
多线程以后怎么办?Java内存模型。
基础篇-使用 Thread/ThreadPool/ReentrantLock/BlockingQueue/volatile/ThreadLocal/AtomicInteger/wait/notify/synchronized
中级篇-原理 JMM/指令重排序/内存屏障/线程饥饿/CAS
高级篇-分析 并行包源码,自己实现,字节码分析,汇编码分析
什么是线程,为什么要多线程
如何创建线程
如何调度多线程
多线程带来了哪些问题:只有共享才有问题,不共享就没问题,哪些变量共享?
ThreadLocal,每个线程不一样,不共享
一个一个解决多线程带来的问题:缓存/指令重排序/死锁/嵌套锁死/重入锁死/虚假唤醒(自旋锁)/饥饿
volatile,一个写,多个读
synchronized,多个写(锁,解锁)
wait/notify,多线程之间的通信
以上已经实现了锁的功能,后面都是如何解决问题和提交效率了:线程饥饿/重入/读 >> 写
Lock显式锁:显示意味着可以控制更多
ReentreLock,可重入锁(解决了饥饿问题?)
读操作也需要锁吗?如果一个写,多个读用volatile就好,不用锁。如果只是读很多,写比较少呢?读写锁出现了
java.util.concurrent包源码分析,JDK1.5以后并行包提供了一切,直接用就行了,最好理解源码和原理
CAS乐观锁(AtomicInteger使用CAS实现)
ConcurrentLinkedQueue
多个线程读,不需要锁;
一个线程写,多个线程读,volatile;
多个线程写,synchronized
读多写少,ReadWriteLock