思维导图地址:
思维导图内容如下:
并发专题
- 并发理论知识
- 并发与并行
- 并行
- 指在同一时刻,有多条指令在多个处理器上同时执行。(多通路同时执行)
- 并发
- 指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行
- 线程基础
- 线程&进程
- 进程:操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。(网易云音乐、腾讯视频)
- 线程:线程,有时被称为轻量级进程(Lightweight Process,LWP),是操作系统调度(CPU调度)执行的最小单位。
- 进程与线程的区别
- 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
- 进程拥有共享的资源,如内存空间等,供其内部的线程共享
- 进程间通信同一台计算机的进程通信称为 IPC,不同计算机之间的进程通信,需要网协议,例如 HTTP线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
- 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低
- 进程间通信的方式
- 信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。
- 管道(pipe)及有名管道(named pipe)
- 管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
- 信号(signal)
- 信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。
- 消息队列(message queue)
- 消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。
- 共享内存(shared memory)
- 可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。
- 信号量(semaphore)
- 主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。
- 套接字(socket)
- 这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。
- 线程的同步互斥
- 线程同步:一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
- 线程互斥:对于共享的进程系统资源,在各单个线程访问时的排它性。
- 线程同步互斥的控制方法
- 临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
- 互斥量:为协调共同对一个共享资源的单独访问而设计的。
- 信号量:为控制一个具有有限数量用户资源而设计。
- 事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
- Java线程
- Java线程的实现方式
- 继承Thread类
- 实现 Runnable 接口
- 使用有返回值的 Callable
- 线程池
- 1.newCachedThreadPool创建一个可缓存线程池程
- 2.newFixedThreadPool 创建一个定长线程池
- 3.newScheduledThreadPool 创建一个定长线程池
- 4.newSingleThreadExecutor 创建一个单线程化的线程池
- Java线程实现原理
- 基于操作系统原生线程模型来实现。都使用与操作系统一对一的线程模型实现
- Java线程的调度机制
- 抢占式线程调度:每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定
- Java线程的生命周期(6种状态)
- NEW(初始化状态)
- RUNNABLE(可运行状态+运行状态)
- BLOCKED(阻塞状态)
- WAITING(无时限等待)
- TIMED_WAITING(有时限等待)
- TERMINATED(终止状态)
- Thread常用方法
- start方法:创建线程,等得到cpu的时间段后则会执行所对应的run方法体的代码
- sleep方法(不会释放对象锁)
- 调用 sleep 会让当前线程从 Running 进入TIMED_WAITING状态,不会释放对象锁
- 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException,并且会清除中断标志
- 睡眠结束后的线程未必会立刻得到执行
- sleep当传入参数为0时,和yield相同
- yield方法(不会释放对象锁)
- yield会释放CPU资源,让当前线程从 Running 进入 Runnable状态,让优先级更高(至少是相同)的线程获得执行机会,不会释放对象锁;
- 假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比它优先级更高的线程;
- 具体的实现依赖于操作系统的任务调度器
- join方法:等待调用join方法的线程结束之后,程序再继续执行
- Java线程的中断机制
- 注意:使用中断机制时一定要注意是否存在中断标志位被清除的情况
- sleep可以被中断 抛出中断异常:sleep interrupted, 清除中断标志位
- wait可以被中断 抛出中断异常:InterruptedException, 清除中断标志位
- interrupt(): 将线程的中断标志位设置为true,不会停止线程
- isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位
- Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle
- Java线程间通信
- volatile
- volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。
- 等待唤醒机制
- cas+park/unpark
- LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”
- sychronized+wait/notify/notifyAll
- reentrantLock+Condition(await/singal/singalAll)
- 管道输入输出流
- Thread.join
- join可以理解成是线程合并,当在一个线程调用另一个线程的join方法时,当前线程阻塞等待被调用join方法的线程执行完毕才能继续执行,所以join的好处能够保证线程的执行顺序,但是如果调用线程的join方法其实已经失去了并行的意义,虽然存在多个线程,但是本质上还是串行的,最后join的实现其实是基于等待通知机制的。
- Java线程的实现方式
- Java线程的生命周期(6种状态)
- start,sleep,join,yield等方法详解
- Java线程的中断机制
- 线程&进程
- Java内存模型(JMM)
- 一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。
- JMM是围绕原子性、有序性、可见性展开的
- 这块如何学?
- 这部分理解并发的三大特性,JMM工作内存和主内存关系,知道多线程之间如何通信的,掌握volatile能保证可见性和有序性,CAS就可以了,后续JVM层面和硬件层面的分析,基础比较薄弱的同学听不懂可以先跳过,从后面的Java锁机制课程听起,掌握常用的并发工具类,并发容器之后再来看JMM这块。
-
- 并发三大特性
- 可见性
- 当一个线程修改了共享变量的值,其他线程能够看到修改的值。
- Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方法来实现可见性的。
- 如何保证可见性
- volatile关键字
- 内存屏障
- synchronized关键字
- Lock
- final关键字
- 有序性
- 程序执行的顺序按照代码的先后顺序执行。
- JVM 存在指令重排,所以存在有序性问题。
- 如何保证有序性
- volatile关键字
- 内存屏障
- synchronized关键字
- Lock
- 指令重排序
- 只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序
- 意义:使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能
- 原子性
- 一个或多个操作,要么全部执行且在执行过程中不被任何因素打断,要么全部不执行。
- 如何保证原子性
- synchronized关键字
- Lock
- CAS
- JMM与硬件内存架构的关系
- Java内存模型与硬件内存架构之间存在差异。硬件内存架构没有区分线程栈和堆。对于硬件,所有的线程栈和堆都分布在主内存中。部分线程栈和堆可能有时候会出现在CPU缓存中和CPU内部的寄存器中。如下图所示,Java内存模型和计算机硬件内存架构是一个交叉关系
- 并发三大特性
- 并发与并行