线程上下文
CPU时间片:分配给每个线程执行的时间
CPU在一个时刻只能运行一个线程
线程上下文
A线程时间片结束时,保存上下文信息。
1.寄存器
2.程序计数器:保存执行指令的位置,每个线程之间都有一个独立的程序计数器。
阻塞和唤醒线程
需要操作系统介入,用户态和内核态之间切换,消耗大量资源。
优化
减少线程上下文切换的出现
1.减少锁资源竞争。
2.减少锁持有时间:别的线程会被阻塞
将与锁无关的代码移出同步代码块
3.减少锁的粒度
使用读写锁
锁分解:如concurrentHashMap的桶
4.CAS,非阻塞乐观锁。
5.减少GC频率,垃圾回收时会产生内存碎片,需要移动存活对象。会暂停线程,导致上下文切换。
线程数量
降低延迟,提高吞吐量
单核情况:只有CPU操作时,会增加性能损耗
多核情况:可提高性能
多线程:提高CPU利用率,4核创建4个线程,
理论上,会设置为4+1个,阻塞时,会用这个额外的线程。
I/O密集型
单核最佳线程为: (I/O操作耗时/CPU计算耗时)+1
无锁 CAS原理
compare and swap:连续不被中断的
实现原理:
V 读写值
A 预期值
B 最新值
非阻塞式同步方式。采取自旋等待,更新失败时,不会进入阻塞。不成功会一直循环执行
长时间不成功,会给CPU带来压力
ABA问题,修改后又被改回来。
解决:增加了一个版本号,每次更新后,会递增版本号
synchronized
1.修饰示例方法:对实例的对象进行加锁
2.修饰类的方法:对类对象加锁
3.修饰代码块:锁括号里的对象进行加锁
内部使用monitor监控,monitor里面有计数器,值不为0时,加锁失败,进入阻塞
Lock接口:基于AQS 抽象对象同步器
可重入:ReentrantLock(true:公平锁) 类
需要手动释放锁
核心参数:
计数器:state,为0时,表示没有被占有
所有者线程:exclusiveOwnerThread
对比:
synchronized:非公平锁,粒度较粗,自动释放锁
ReentrantLock:可指定公平还是不是公平的,提供中断等待,手工释放锁。
线程池
1.降级线程使用过程中频繁创建和销毁带来的消耗。
2.对线程统一分配和调用。
3.提高了任务响应速度,无需等待线程创建。
原理:
1.线程池内部,维护一个阻塞队列。
2.线程池内部维护工作线程会消费队列中的任务。
使用:
1.ThreadpoolExecutor类,七大参数
ThreadPoolExecutor(
int corePoolSize, 核心线程数,最小线程数。会一直存活。不会被销毁。
int maximumPoolSize, 最大线程数
long keepAliveTime, 长度,超过这个时间,会被回收
TimeUnit unit, 单位
BlockingQueue<Runnable> workQueue, 工作队列
ThreadFactory threadFactory, 线程工厂,自定义设置
RejectedExecutionHandler handler) 拒绝策略
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 25, 100L,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());
CallerRunsPolicy:提交的线程自己执行
AbortPolicy:默认拒绝策略,直接丢弃任务,抛出异常:RejectedExecutionException
DiscardPolicy:直接丢弃任务。
DiscardOldestPolicy:丢弃最老的任务,新任务加入到工作队列中
LinkedBlockingQueue:默认最大任务数是:Interger.Max_VALUE
任务越来越多,会导致OOM,内存无法分配空间给新对象。
内存泄漏:申请的内存没有被释放。
内存溢出:申请的超过已有的。
volatile
共享变量,当被一个线程修改了,别的线程会感知该修改操作
会将自己工作内存中的缓存置为失效,强制从内存中读取改共享变量。
通过禁止指令重排,保证有序性
非原子性操作不适用于volatile
使用场景:
1.单例模式
2.程序终止或启动
ThreadLocal
为每一个线程,创建一个自己的变量副本,可以改变自己的副本,不会改变别的副本。
属于当期线程有效。
可能会内存泄漏。
使用完成后,需要置为null进行回收