名词解释
并发 vs 并行
并发(concurrent):一个处理器同时处理多个任务,无论上一个开始执行的任务是否完成,当前任务都可以开始执行,相反的概念是顺序(sequential)上一个开始执行的任务完成后,当前任务才能开始执行。
并行(parallel):多个处理器或者是多核的处理器同时处理多个不同的任务,多个任务执行单元,相反的概念是串行(serial)只有一个任务执行单元。
同步 vs 异步
同步:执行某个操作开始后就一直等着按部就班的直到操作结束
异步:执行某个操作后立即离开,后面有响应的话再来通知执行者
阻塞 vs 非阻塞
阻塞:某个操作需要的共享资源被占用了,只能等待,称为阻塞
非阻塞:某个操作需要的共享资源被占用了,不等待立即返回,并携带错误信息回去,期待重试
临界区:公共资源或者共享数据
Java 并发机制的底层实现
原子性(Atomicity):不可被中断的一个或一系列操作。
可见性(Visibility):当一个线程修改一个变量的值,新值对于其它线程是可以立即得知的。
顺序性(Ordering):如果在线程内观察,所有的操作都是有序的,如果在一个线程中观察另一个线程,所有操作都是无序的。“线程内表现为串行的语义”(Within-Thread As-If-Serial Semantics),“指令重排序”现象和“工作内存和主内存同步延迟”现象。
volatile
volatile 变量,用于确保将变量的更新操作同步到其它线程。volatile 变量是一种比 synchronized 关键字更轻量级的同步机制。
变量定义为volatile 后,它将具备两种特性:
1、变量可见性,当一个线程修改一个变量时,另一个线程能读到这个修改的值。
2、禁止指令重排序优化,编译器重排序,处理器重排序。
synchronized
一个变量在同一个时刻只允许一条线程对其进行 lock 操作。
- 原理:Monitor 对象,monitorenter, monitorexit 配对。
- 实现同步的基础:
对于普通方法,锁的是当前实例对象。
对于静态同步方法,锁的是当前类的 Class 对象。
对于同步方法块,锁的是 synchronized 括号里配置的对象。 - 做了哪些优化?
自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
CAS(Compare And Swap)
- 定义:比较并交换,乐观锁机制
- 使用场景:原子包 java.util.concurrent.atomic(锁自旋)
- CAS 三大问题:
ABA 问题
循环时间长开销大
只能保证一个共享变量的原子操作
Java 并发编程的基础
线程简介
什么是线程
是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
为什么要使用多线程
更多的处理器核心
更快的响应时间
更好的编程模型
线程的优先级
线程的状态
状态名称 |
说明 |
NEW |
尚未启动的线程状态,即线程创建,还未调用start方法 |
RUNNABLE |
就绪状态(调用start,等待调度)+正在运行 |
BLOCKED |
等待监视器锁时,陷入阻塞状态 |
WAITING |
等待状态的线程正在等待另一线程执行特定的操作(如notify) |
TIMED_WAITING |
具有指定等待时间的等待状态 |
TERMINATED |
线程完成执行,终止状态 |
线程的状态变迁
创建、启动、运行、终止线程
JAVA线程实现/创建方式
继承Thread类,实现Runnable接口
start 与 run 区别
终止线程 4 种方式
正常运行结束
使用退出标志退出线程
Interrupt 方法结束线程
stop 方法终止线程(线程不安全)
线程基本方法
wait,notify,notifyAll,sleep,join,yield
线程上下文切换
进程
上下文
寄存器
程序计数器
PCB process control block-“切换桢”
上下文切换的活动
引起线程上下文切换的原因
线程调度方式
抢占式,协同式
线程间的通信
volatile 和 synchronized
等待/通知机制
管道输入/输出流
Thread.join() 的使用
ThreadLocal
Java中的线程池
线程池的好处
- 降低资源消耗;
- 提高响应速度;
- 提高线程的可管理性;
线程池的实现原理
ThreadPoolExecutor 执行示意图
ThreadPoolExecutor 执行任务示意图
线程池的使用
线程池的创建
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
@NotNull TimeUnit unit,
@NotNull BlockingQueue<Runnable> workQueue,
@NotNull ThreadFactory threadFactory,
@NotNull RejectedExecutionHandler handler)
corePoolSize – the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set
maximumPoolSize – the maximum number of threads to allow in the pool
keepAliveTime – when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
unit – the time unit for the keepAliveTime argument
workQueue – the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.
threadFactory – the factory to use when the executor creates a new thread
handler – the handler to use when execution is blocked because the thread bounds and queue capacities are reached
当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。
向线程池提交任务
调用 execute() 提交不需要返回值的任务。
调用 submit() 提交需要返回值的任务。
关闭线程池
调用 shutdown() 或者 shutdownNow() 方法。
原理是遍历线程池中的工作线程,然后逐个调用线程的 interrupt 方法中断线程,所以无法响应中断的任务可能永远无法终止。
合理配置线程池
线程池监控
参数配置
corePoolSize >= requestsPerSecond * secondsPerRequest
maximumPoolSize >= maximumRequestsPerSecond * secondsPerRequest
queueCapacity <= maximumPoolSize * maxWaitTime / timePerRequest