线程
1. 线程
1.1 进程/线程
进程:程序运行起来的状态,OS分配资源的基本单位
线程:进程中的不同执行路径,是执行调度的基本单位
1.2 线程生命周期
- new:使用new关键字创建线程,jvm分配内存,并初始化成员变量的值
- runnable:start时,线程处于就绪状态,jvm创建方法调用栈和程序计数器,等待调度运行
- running:run方法,处于就绪状态的线程获取到了cpu,开始执行任务
- blocked:阻塞状态,线程放弃cpu的使用权
- dead:死亡,线程正常执行完毕/抛异常/stop(),容易死锁
2. 多线程
2.1 线程池
2.1.1 线程池类型
- newCachedThreadPool:可缓存线程池,线程数量超过可在指定时间内回收
- newFixedThreadPool:固定线程数线程池,可以设置固定线程数,一般设置为与CPU数一致,方便管理线程
- newScheduledThreadPool:定时线程池
- newSingleThreadExecutor:单一线程池,可以在这个线程死后用重启另一个线程继续执行任务
2.1.2 线程池(ExecutorService)的参数
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:非核心线程的空闲线程存活时间
- TimeUnit:时间单位
-
workQueue:缓存任务的阻塞队列
- threadFactory:线程工厂,一般默认
- handler:拒绝策略,最大线程数和阻塞队列都满了,新的任务采用拒绝策略
- 默认,抛异常
- 丢弃,丢弃新的任务
- 丢弃最老的,丢弃在队列中存活时间最久的,再将新的任务放入队列
- 运行,用主线程执行任务
2.1.3 线程池执行顺序
- corePoolSize未满,创建核心线程执行任务
- corePoolSize满了,workQueue未满,将任务放入阻塞队列
- workQueue满了,maximumPoolSize未满,创建工作线程执行任务
- maximumPoolSize满了,新的任务根据定义的拒绝策略来处理
3. 线程通信
共享内存/消息传递/管道流
3.1 volatile
关键字,保证内存可见性,禁止指令重排
原理:
普通变量进行读写时,每个线程从内存中拷贝变量到CPU缓存中,计算机有多个CPU,每个线程可能在不同的CPU上处理,
带有volatile关键字变量的值被一个线程修改后,会将变量的值写回主内存,其他线程直接从内存中读取变量。
禁止指令重排:
在用volatile修饰的变量上增加内存屏障,读为Load,写为Store,分为四种内存屏障指令
- LoadLoad
- StoreStore
- LoadStore
- StoreLoad
确保Load数据读取在其他后续操作之前
确保Store数据写操作对其他CPU可见,刷新到内存
3.2 wait/notify
wait和sleep的区别:
wait属于Object类 sleep属于Thread类
wait是会释放锁,对象进入等待池,让出资源 sleep不会释放锁,不会让出系统资源,暂停执行任务
wait线程进入等待池,需要使用nofity来唤醒 sleep指定时间自动唤醒,未到指定时间,可以使用interrupt中断
wait必须配合synchronized使用,wait会强迫线程释放锁,所以在调wait之前必须先持有锁,否则会抛异常
4. Synchronized
synchronized 的含义:
- Java中每一个对象都可以成为一个监视器
(Monitor)
, 该Monitor由一个锁(lock)
, 一个等待队列(waiting queue)
, 一个入口队列(entry queue)
组成。 - 对于一个对象的方法, 如果没有synchronized关键字修饰, 该方法可以被任意数量的线程,在任意时刻调用。
对于添加了synchronized关键字的方法,任意时刻只能被唯一的一个获得了对象实例锁的线程调用。 - synchronized用于实现多线程的同步操作
synchronized锁升级
对象头MarkWord锁标志位,