2.2 多线程
2.2.1 锁
- Java对象头和Monitor
- 对象在内存分为, 对象头、实例数据和对齐填充
- 对象头实现synchronized, 轻量锁和重量锁在
Mark Word
结构中定义 ObjectMonitor
,wait
状态的线程加入_WaitSet
, 等待锁block
状态的线程加入_EntryList
- monitor对象在于每个对象的Java头, 因此Java任意对象都可以作为锁
- synchronized代码块底层原理
monitorenter
指令, 进入同步方法,monitorenter
则通过操作系统的互斥锁实现monitorexit
指令, 退出同步方法
- synchronized方法底层原理
- 隐式, 无需通过
monitorenter
和monitorexit
字节码指令控制 - 方法表结构中
ACC_SYNCHRONIZED
标识
- 隐式, 无需通过
- Java虚拟机synchronized的优化
- 偏向锁
- 线程A获得锁, 锁即进入偏向模式,
Mark Word
变为偏向锁结构, 线程A再次请求锁, 无需操作即获得锁
- 线程A获得锁, 锁即进入偏向模式,
- 轻量级锁
- 线程交替执行同步块的场景
- 自旋锁
- 避免用户态和内核态的切换
- 锁消除
- 优化没必要的锁
- 偏向锁
- synchronized的可重入性
- 对相同对象请求锁, 是允许的, 即synchronized的可重入性
- 线程中断和synchronized
- 线程中断操作对synchronized不起作用
- 等待唤醒机制与synchronized
notify()
,notifyAll()
,wait()
方法必须在synchronized
代码块内或者方法中
2.2.2 CAS (Compare And Swap)[V, E, N]
- CPU指令对CAS的支持
- CAS是CPU的原子指令
- UnSafe指针
Unsafe#compareAndSwapObject()
Unsafe#compareAndSwapInt()
Unsafe#compareAndSwapLong()
- 并发中的Atomic
- 原子更新基本类型
- AtomicBoolean
- AtomicInteger
- AtomicLong
- 原子更新引用
- 原子更新数组
- 原子更新属性
- 自旋锁
- 原子更新基本类型
2.2.3 wait/notify、sleep/
Object#wait()
通过监视锁对象完成, 需要先获得monitor
的所有权, 通过synchronized
关键字完成,Object#notify()
同理wait
和sleep
的区别sleep
不影响锁的状态,wait
释放锁- 调用
wait
之后需要调用notify
来获取CPU资源 yield
让CPU调度, 类似sleep
只是不能指定时间join
, Thread A调用Thread B#join
, A需要等B结束才能执行
2.2.4 ThreadLocal
- ThreadLocal存放到ThreadLocalMap
2.2.5 线程池实现原理 Executor
- ThreadPoolExecutor
- corePoolSize 核心线程数
- maximumPoolSize 维护最大线程数
- keepAliveTime 空闲线程存活时间
- workQueue 任务队列
- threadFactory 线程工厂
- handler 拒绝策略
- 创建规则(corePoolSize最小存活线程数量[除非allowCoreThreadTimeOut为true])
-
- 线程数量小于corePoolSize, 创建线程处理任务
-
- 线程大于corePoolSize并小于maximumPoolSize之间, workQueue未满, 缓存新任务
-
- 线程小于maximumPoolSize且workQueue满了, 创建新线程处理任务
-
- 线程大于maximumPoolSize且workQueue满了, 使用拒绝策略
-
- 资源回收
- 排队策略
- 拒绝策略
- AbortPolicy (默认, 直接抛异常)
- CallerRunsPolicy (只用调用者所在的线程执行任务)
- DiscardOldestPolicy (丢弃任务队列中最久的任务)
- DiscardPolicy (丢弃当前任务)
- Executors
- ThreadPoolExecutor
Executors#newFixedThreadPool(int nThreads) -> new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
- corePoolSize&maximumPoolSize数量均为nThreads
- keepAliveTime为0, 即空闲即停止
- LinkedBlockingQueue无界队列, 即不拒绝任务
Executors#newCachedThreadPool() -> new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>())
, 对短时间任务性能较好- corePoolSize为0, maximumPoolSize为无限大
- keepAliveTime为60S
- SynchronousQueue, 无存储空间, 无空闲线程则创建新线程
Executors#newSingleThreadExecutor() -> new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue)
- corePoolSize&maximumPoolSize数量均为1, 即创建一条线程处理任务
- LinkedBlockingQueue无界限队列任务
Executors#newScheduledThreadPool(int corePoolSize)
corePoolSize
核心线程数
- ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor#scheduleWithFixedDelay
, 固定延迟执行ScheduledThreadPoolExecutor#scheduleAtFixedRate
, 固定频率执行
- ThreadPoolExecutor
2.2.6 Lock & ReentrantLock
Lock
与synchronized
的区别Lock
提供可中断获取, 超时中断获取Lock
提供等待唤醒机制的多条件变量Condition
- ReentrantLock(重入锁)
- 使用:
ReentrantLock reentrantLock = new ReentrantLock(); try { reentrantLock.lock(); } finally { reentrantLock.unlock();// lock几次, 就要unlock几次 }
- 原理
AbstractQueuedSynchronizer
,包含Node head
同步队列头,Node tail
同步队尾,int state
0表示锁未被占用, 1表示锁被占用,Node
, 包含Node SHARED = new Node()
共享模式,Node EXCLUSIVE = new Node()
独占模式,AbstractQueuedSynchronizer
提供共享和独占模板给子类实现, 包含tryAcquire()
和tryAcquireShared()
Sync
,ReentrantLock
的内部类, 继承AbstractQueuedSynchronizer
NonfairSync
,ReentrantLock
的内部类, 非公平锁FairSync
, 公平锁ReentrantLock#lock()
->NonfairSync#lock()
- ->
compareAndSetState(0, 1)
执行CAS操作, 获取同步状态, ->setExclusiveOwnerThread(Thread.currentThread)
设置独占线程为当前线程 - else ->
acquire(1)
- ->
tryAcquire()
- ->
nonfairTryAcquire()
compareAndSetState()
再次获取同步状态current == getExclusiveOwnerThread()
判断当前线程是否是ownerThread
, 重入锁
- ->
- ->
acquireQueued(addWaiter(Node#EXCLUSIVE))
Node#addWaiter()
->Node#enq()
, 将新节点增加到队尾acquireQueued()
-> 自旋过程, 满足条件p == head && tryAcquire()
并获取同步状态
- ->
Condition
包含await()
,signal()
- ->
Semaphore
, 共享锁-
使用:
Semaphore semp = new Semaphore(5); try { semp.acquire(); } finally { semp.release(); }
-
- 读写锁, 读锁不互斥, 写锁与其它均互斥
- 自旋锁, 未获得锁则不断重试直到拿到锁
2.2.7 volatile
- Java内存区域
- 方法区
- 虚拟机栈
- 本地方法栈
- 堆
- 程序计数器
- Java内存模型
- 原子性
- 重排序
- 编译器优化重排序, 不改变单线程的语义下, 重排语句的执行顺序
- 指令并行重排
- 内存系统的重排
- 可见性, 在多线程环境下, 重排导致程序轮序执行的问题, 导致可见性问题
- 有序性
synchronized
可解决原子性, 可见性, 有序性问题,volatile
可解决可见性, 有序性问题
- JMM的happens-before原则[辅助保证程序执行的原子性、可见性以及有序性]
- volatile内存语义
- 保证volatile修饰的变量修改的新值对所有线程可见
- 禁止指令重排序
2.2.8 BlockQueue实现原理
ArrayBlockingQueue
通过ReentrantLock
和Condition
实现
2.2.9 线程的状态
- New, 新建状态, 还未调用
start()
时 - Ready, 就绪状态, 调用
start()
之后或者调用yield
- Running, 运行状态, 获得CPU资源
- BLOCKED, 阻塞状态, 等待锁, 进入
synchronized
方法或者synchronized
块 - TIMED Waiting, 超时等待, 调用
sleep(long)
,join(long)
,wait(long)
- Waiting, 等待状态, 调用
wait()
,join()
- Terminated, 结束状态
图片来自知乎Dawell的答案:
2.2.10 AsyncTask
- 线程池
// 串行线程池, 处理任务入队
// Executor sDefaultExecutor = new SerialExecutor();默认执行时调用
private static class SerialExecutor implements Executor {
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
public static final Executor THREAD_POOL_EXECUTOR;
// CORE_POOL_SIZE根据CPU核数创建
// runnable执行线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
// 执行任务
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
// 执行任务
exec.execute(mFuture);
}
线程
- 线程和进程的区别
- 进程是运行中的程序,线程是进程的内部的一个执行序列
- 进程是资源分配的单元,线程是执行行单元
- 进程间切换代价大,线程间切换代价小
- 进程拥有资源多,线程拥有资源少 多个线程共享进程的资源
- 内核线程
- 内核线程就是内核得分身, 一个分身可以处理一个特定事情。这在处理异步事件如异步IO时特别有用。内核线程得使用是廉价的, 唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间, 支持多线程的内核叫做多线程内核(MultiThreads kernel)
- 轻量级线程
- 轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程, 它是内核线程的高度抽象, 每一个轻量级进程都有一个特定的内核线程关联, 内核线程只能由内核管理并像普通进程一样被调度
- 用户线程
- 用户线程是完全建立在用户空间的线程库, 用户线程的创建、调度、同步和销毁全由库函数在用户空间完成, 不需要内核的帮助, 因此这种线程是极其低消耗和高效的