【Java】多线程精简笔记
-
进程和线程
- 进程是系统资源分配的基本单位,线程是系统调度的基本单位
- 进程是程序正在运行的实例,里面包含着线程
- 进程有独立的内存和资源,线程共用内存(工作内存独有,主内存共用)和资源
- 线程比进程更加轻量,线程们共用上下文
-
并行和并发
- 并行:多个CPU处理多个线程
- 并发:一个CPU轮询处理多个线程
-
线程类Thread的基本属性
- 优先级:被CPU处理的概率更高
- 后台线程:后台线程结束后,JVM才结束运行
-
线程的创建方法
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- lambda表达式创建线程的匿名类对象
-
Runnable和Callable的区别
- 前者没有返回值,后者有返回值
- 前者不能抛出异常,后者可以抛出异常
-
Thread的常用方法
方法 | 类型 | 介绍 |
---|
run() | 实例 | 线程执行逻辑 |
start | 实例 | 启动线程 |
currentThread() | 类方法 | 获得当前线程对象引用 |
join() | 实例 | 不释放锁,被打断后会恢复至不中断,需捕获异常再次中断 |
sleep() | 类方法 | 不释放锁,被打断后会恢复至不中断,需捕获异常再次中断 |
isInterrupted() | 实例 | 仅判断线程是不是中断 |
interrupt() | 实例 | 中断线程,等待和阻塞的会报异常 |
interrupted() | 类方法 | 判断是不是处于中断状态,会恢复中断状态至不中断 |
方法 | 类型 | 介绍 |
---|
wait() | 实例 | 会释放锁,打断后不恢复 |
notify() | 实例 | 唤醒一个等待此对象的线程 |
notifyAll() | 实例 | 唤醒所有等待此对象的线程 |
-
run和start的区别
- run是线程对象的实例方法,使用run就是正常的调用对象方法,而不是启动线程
- start就是启动线程并且执行run方法
-
notify和notifyAll的区别
- notify是随机唤醒一个
- notifyAll是全部唤醒
-
sleep和wite的区别
- wite是Object的实例方法,sleep是Thread的类方法
- sleep和wite被打断抛出的异常不同
- sleep被打断后会恢复至不打断,需捕获异常再次中断
- wait会释放锁让出CPU,sleep不会
-
如何停止一个线程
- volatile修饰的标志量
- interrupt()直接打断,会报异常
-
如何让线程有顺序的执行
- 使用线程的join方法
- 使用主线程的join方法
- 使用线程的wait方法
- 使用线程的线程池方法
- 使用线程的Condition(条件变量)方法
- 使用线程的CountDownLatch(倒计数)方法
- 使用线程的CyclicBarrier(回环栅栏)方法
- 使用线程的Semaphore(信号量)方法
-
线程之间如何通讯
- 线程通讯指的是多个线程之间通过共享内存或消息传递等方式来协调和同步它们的执行
- wait() 和 notify() 的等待和通知机制
- Semaphore 信号量机制:
一种控制对有限数量的共享资源访问的机制。它可以用来限制并发访问的数量
- CyclicBarrier 栅栏机制:
一组线程可以在一个共同点(栅栏)处相互等待,直到最后一个线程到达,所有线程才会继续执行。
- 以及 Condition 的锁机制:
一种控制对有限数量的共享资源访问的机制。它可以用来限制并发访问的数量
-
线程状态
状态 | 描述 |
---|
new | 创建好线程,尚未启动 |
runnable | 正在执行的 |
blocked | 阻塞等待的 |
waiting | 等待的 |
timed_waiting | 定时等待 |
terminated | 终止状态 |
特性 | 描述 | 解决方法 |
---|
原子性 | 操作不可再分,也不可被影响 | 加锁 |
内存可见性 | 一个线程对内存数据的修改必须让其它线程可知 | 加锁,volatile |
有序性 | 指令按照原本的顺序执行 | volatile |
-
volatile
- 禁止编译器对变量的优化
- 保证内存可见性
- 禁止指令重排序
- 写时屏障对上,读时屏障对下
-
synchronized原理,对象是如何与锁关联
- Monitor(JVM,C++)
- Owner,获取锁的线程
- EntryList,处于“Blocked”的线程
- WaitSet,处于“Waiting”的线程
- 对象内存的对象头
-
synchronized 锁升级机制也叫做锁膨胀机制
- 无锁 → 偏向锁 → 轻量级锁 → 重量级锁
- 锁可以升级但不能降级
锁 | 解释 |
---|
无锁 | 没有线程来获取锁 |
偏向锁 | 第一个来获锁的线程 |
轻量级锁 | 多个线程获取锁,但彼此间竞争不重,自旋等待 |
重量级锁 | 自旋超过限度,锁竞争激烈,相互互斥 |
-
CAS
- Compare And Swap(比较再交换)
- 比较x1和x2,同则将x1修改为x3
- CPU上的一个原子性指令,操作都是原子的
- 可以实现乐观锁
-
AQS
- AbstractQueuedSynchronizer(抽象队列同步器)
- 实现的锁策略
部件 | 描述 |
---|
state | 状态码,0无锁,1占有,>1线程重入多次 |
FIFO队列 | 先进先出队列,阻塞中的线程 |
-
ReentrantLock
- 可重入锁,公平锁,不公平锁
- CAS + AQS,继承与AQS
部件 | 描述 |
---|
state | 状态码,0无锁,1占有,>1线程重入多次 |
FIFO队列 | 先进先出队列,阻塞中的线程 |
exclusiveOwnerThread | 当前占住锁的线程 |
synchronized | lock |
---|
关键字 | 接口 |
能实现的锁策略有限 | 能提供更多类型的锁 |
使用简单,性能不错 | 指定适合的锁,能提供更好的性能 |
锁 | 描述 |
---|
共享锁/排他锁(读写锁) | 读可多个线程得,写仅一个线程得 |
公平锁/非公平锁 | 先到先得,无序竞争 |
乐观锁/悲观锁 | 修改失败则重试,禁止其它修改 |
轻量级锁/重量级锁 | 操作系统的互斥量(Mutex),前者不阻塞其它队列,后者阻塞 |
可重入锁 | 同个线程可获取多次锁 |
偏向锁 | 不加锁,仅标记首次获取锁的线程 |
自旋锁 | 不阻塞,一直尝试获取锁 |
互斥锁 | 仅一线程可获取锁,阻塞其它线程 |
锁 | 简介 |
---|
ReentrantLock | 可重入锁 |
ReentrantReadWriteLock | 悲观读写锁 |
StampedLock | 乐观读写锁 |
-
死锁
- 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放
- 死锁的解决方法
- 死锁的排查方法
- 借助jps,jstack,jconsole,jvisualvm等工具
- 死锁四大条件
条件 | 解释 |
---|
互斥使用 | 资源被某线程使用后,其它线程不能使用 |
不可抢占 | 请求者不能从占有者抢占资源,只能等占有者自行释放 |
请求和保持 | 请求其它资源时保持对现有资源的占有 |
环路等待 | 等待资源时出现了一个环路,A需要B占资源,B需要A占资源 |
-
ConcurrentHashMap的底层原理
- JDK1.7,Segment分段锁,底层使用的是ReentrantLock
- JDK1.8,CAS添加新节点,采用synchronized锁定链表或红黑二叉树的首节点,相对Segment分段锁粒度更细,性能更好
-
ConcurrentHashMap为什么不能插入Null
- 二义性,是没有键值对?还是value为null?
- 多线程环境下导致内存不可见
-
ThreadLocal
- 哈希表,key是线程id,value是资源的独立副本
- 内存泄漏
- key 是弱引用,值为强引用
- key 会被GC 释放内存,关联 value 的内存并不会释放
- 主动remove删除键值对
- 使用FastThreadLocal
参数 | 解释 |
---|
corePoolSize | 核心线程数(N+1和2N+1) |
maximumPoolSize | 最大线程数 |
keepAliveTime | 空闲工作线程的存活时间 |
unit | 时间单位 |
workQueue | 存储任务的阻塞队列 |
threadFactory | 线程创建工厂 |
handler | 处理方式,拒绝策略 |
拒绝策略 | 解释 |
---|
AbortPolicy | 直接抛出异常,默认策略 |
CallerRunsPolicy | 用调用者所在的线程来执行任务 |
DiscardOldestPolicy | 丢弃阻塞队列中靠最前的任务,并执行当前任务 |
DiscardPolicy | 直接丢弃任务 |
阻塞队列 | 解释 |
---|
ArrayBlockingQueue | 基于数组结构的有界阻塞队列,FIFO |
LinkedBlockingQueue | 基于链表结构的有界阻塞队列,FIFO |
DelayedWorkQueue | 是一个优先级队列,它可以保证每次出队的任务都是当前队列中执行时间最靠前的 |
SynchronousQueue | 不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作 |
-
LinkedBlockingQueue和ArrayBlockingQueue的区别
LinkedBlockingQueue | ArrayBlockingQueue |
---|
默认无界,支持有界 | 强制有界 |
底层是链表 | 底层是数组 |
是懒惰的,创建节点的时候添加数据 | 提前初始化Node数组 |
入队会生成新 Node | Node需要是提前创建好的 |
两把锁(头尾) | 一把锁 |
描述 | 线程池创建方法 |
---|
固定线程数的线程池 | newFixedThreadPool() |
单个线程的线程池 | newSingleThreadExecutor() |
可缓存的线程池 | newCachedThreadPo0l() |
可延迟,可周期的线程池 | newScheduledThreadPool() |
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1985076.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!