锁升级
在JVM底层实现锁的过程中,有三类锁:偏斜锁、轻量级锁、重量级锁
- 在Java6之前,synchronized的实现完全依靠重量级锁(系统内的互斥锁),从用户态转为内核态非常消耗资源。
- 在Java6之后,提供了三种不同Monitor实现,偏斜锁、轻量级锁、重量级锁。
所谓的升级、降级,就是JVM优化synchronized运行的机制,当JVM检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级、降级。
偏向锁
- 只有单线程执行情况下,该线程在后续访问时,便会自动获取锁。
- 当没有线程并发出现时,会默认使用偏向锁。JVM会利用CAS操作,在对象头上的Mark Word部分设置线程ID,表示这个对象偏向于当前线程。无竞争开销。
轻量级锁
轻量级锁不支持并发,遇到1并发转为重量级锁。
支持多个线程以串行的方式访问同一个加锁对象。每次执行完,都消耗了重复加锁与解锁的性能开销。
锁的状态保存在对象头中。在Hotstop虚拟集中,一个JAVA对象的存储结构,在内存中的存储布局分为3块区域:对象头(Header)、实例数据(Instance data)和对其填充(Padding)。
轻量级锁加锁的过程:
在代码进入同步块的时候,如果对象锁状态为无锁状态(01),即将进入的当前线程先创建lock_record的空间,存储当前对象的Mark Work的拷贝,命名为Displaced Mark Word。
拷贝成功后,对象Mark Word中的prt_to_lock_record指向线程中的lock record,并将lock record 里的owner指针指向对象的 Mark Word。若执行成功,并将无状态(01)修改为有状态(00)。若执行失败,判断当前对象锁Mark Word是否已经指向当前线程。是,当前线程已拥这个对象的锁,否则多个线程竞争该锁,直接接升级为重量级锁,状态值也会变为10。当前线程尝试使用自旋来获取锁。(不断尝试机制)
轻量级锁的解锁过程:通过CAS指令,尝试把线程中复制的Displaced Mark Word对象替换当前的Mark Word。
若替换成功,整个同步过程就完成了。
如果替换失败,说明有其他线程尝试过获取该锁,该锁已升级为重量级锁,要在释放锁的同时,通知其他线程重新参与所得竞争。
重量级锁
依赖于操作系统互斥锁所实现的锁。操作系统的互斥锁实现线程之间的切换,需从用户态转为内核态。切换成本非常高,状态之间的转换需要相对比较的时间。
线程池
线程池是用于管理和复用线程的一种技术,可以优化线程的执行效率。因为,频繁的创建和销毁线程,会带来额外的系统开销,而线程池可以通过创建并重用一组线程来执行任务,可以显著减少这种开销,提高程序的性能和响应能力。
线程池的基本流程:
应用程序提交线程任务给线程池,判断线程池是否有空闲线程去执行任务,若有,创建新线程就去执行,若没有,比较当前运行的线程数和核心线程数,若小于,线程池会创建一个新线程去执行线程任务,若大于,线程池会检查工作队列是否满,若没满,放入队列进行等待,线程池出现空闲线程,将从队列中按照先进先出规则取出线程任务并分配执行;若已满,判断线程数是否达到最大线程数,若没达到,线程池创建新线程(非核心线程)执行线程任务;若已达到,直接采用拒绝策略处理新线程任务。
执行顺序:核心线程、工作队列、非核心线程、拒绝策略。
如何创建线程池?
通过Executors工具类的静态方法,创建线程池
创建ThreadPoolExecutor对象,按照业务需求,自定义线程池参数,创建线程池
线程池的核心配置参数有哪些?
corePoolSize:线程池的核心线程数
maximumPoolSize:线程池的最大线程数
KeepAliveTime:非核心线程存活时间
threadFactory:创建新线程的线程工厂
handler:线程池的决绝策略
线程池分类
FixedThreadPool:线程固定的线程池,使用Executors.newFixedThreadPool()创建;
CachedThreadPool:线程数根据任务动态调整的线程池,使用Executors.newCachedThreadPool创建
SingleThreadExecutor:进提供一个单线程的线程池,使用Executors.newSingleThreadExecutor()创建;
ScheduledThreadPool:能实现定时、周期性任务的线程池,使用Executors.newScheduledThreadPool()创建
线程池的状态
- 线程池的状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED;
- RUNNING运行状态:
- 线程池一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0.该状态的线程会接受新任务,并处理工作队列中的任务。
- 调用线程池的shutdown()方法,可以切换到shutdown状态;
- 调用线程池的shutdownNow()方法,可以切换到stop状态;
- SHUTDOWN关闭状态:
- 该状态的线程池不会接受新任务,但会处理工作队列中的任务;
- 当工作队列为空时,并且线程池中执行的任务也为空时,线程进入TIDYING状态;
- STOP停止状态:
- 该状态的线程不会接受新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
- 线程池中执行的任务为空,进入TIDYING状态
- TIDYING整理状态:
- 该状态表明所有的任务已经运行终止,记录的任务数量为0
- TERMINATED终止状态:该状态表名线程彻底关闭。
线程池拒绝策略
- AbortPolicy:默认策略,丢弃任务并抛异常
- DiscardPolicy:丢弃任务,不抛异常
- DiscardOldestPolicy:丢弃对了中的最早的任务(最早入队的),重新将新任务提交给线程
- CallerRunsPolicy:提交线程任务的只执行run()方法,线程池不负责处理。