锁升级及线程池相关

锁升级

在JVM底层实现锁的过程中,有三类锁:偏斜锁、轻量级锁、重量级锁

  • 在Java6之前,synchronized的实现完全依靠重量级锁(系统内的互斥锁),从用户态转为内核态非常消耗资源。
  • 在Java6之后,提供了三种不同Monitor实现,偏斜锁、轻量级锁、重量级锁。

所谓的升级、降级,就是JVM优化synchronized运行的机制,当JVM检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级、降级。

偏向锁

  • 只有单线程执行情况下,该线程在后续访问时,便会自动获取锁。
  • 当没有线程并发出现时,会默认使用偏向锁。JVM会利用CAS操作,在对象头上的Mark Word部分设置线程ID,表示这个对象偏向于当前线程。无竞争开销。

轻量级锁

轻量级锁不支持并发,遇到1并发转为重量级锁。

支持多个线程以串行的方式访问同一个加锁对象。每次执行完,都消耗了重复加锁与解锁的性能开销。

锁的状态保存在对象头中。在Hotstop虚拟集中,一个JAVA对象的存储结构,在内存中的存储布局分为3块区域:对象头(Header)、实例数据(Instance data)和对其填充(Padding)。

轻量级锁加锁的过程:

  1. 在代码进入同步块的时候,如果对象锁状态为无锁状态(01),即将进入的当前线程先创建lock_record的空间,存储当前对象的Mark Work的拷贝,命名为Displaced Mark Word。

  2. 拷贝成功后,对象Mark Word中的prt_to_lock_record指向线程中的lock record,并将lock record 里的owner指针指向对象的 Mark Word。若执行成功,并将无状态(01)修改为有状态(00)。若执行失败,判断当前对象锁Mark Word是否已经指向当前线程。是,当前线程已拥这个对象的锁,否则多个线程竞争该锁,直接接升级为重量级锁,状态值也会变为10。当前线程尝试使用自旋来获取锁。(不断尝试机制)
    轻量级锁的解锁过程:

  3. 通过CAS指令,尝试把线程中复制的Displaced Mark Word对象替换当前的Mark Word。

  4. 若替换成功,整个同步过程就完成了。

  5. 如果替换失败,说明有其他线程尝试过获取该锁,该锁已升级为重量级锁,要在释放锁的同时,通知其他线程重新参与所得竞争。

重量级锁

依赖于操作系统互斥锁所实现的锁。操作系统的互斥锁实现线程之间的切换,需从用户态转为内核态。切换成本非常高,状态之间的转换需要相对比较的时间。

线程池

线程池是用于管理和复用线程的一种技术,可以优化线程的执行效率。因为,频繁的创建和销毁线程,会带来额外的系统开销,而线程池可以通过创建并重用一组线程来执行任务,可以显著减少这种开销,提高程序的性能和响应能力。

线程池的基本流程:

应用程序提交线程任务给线程池,判断线程池是否有空闲线程去执行任务,若有,创建新线程就去执行,若没有,比较当前运行的线程数和核心线程数,若小于,线程池会创建一个新线程去执行线程任务,若大于,线程池会检查工作队列是否满,若没满,放入队列进行等待,线程池出现空闲线程,将从队列中按照先进先出规则取出线程任务并分配执行;若已满,判断线程数是否达到最大线程数,若没达到,线程池创建新线程(非核心线程)执行线程任务;若已达到,直接采用拒绝策略处理新线程任务。

执行顺序:核心线程、工作队列、非核心线程、拒绝策略。

如何创建线程池?

  • 通过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()方法,线程池不负责处理。
相关推荐
luoluoal1 分钟前
基于Spring Boot的装饰工程管理系统源码(springboot)
java·spring boot·后端
一个会的不多的人7 分钟前
C# Solidworks二次开发:宏录制实战讲解(第二讲)
开发语言·c#
J不A秃V头A17 分钟前
IDEA实用小技巧:方法之间的优雅分割线
java·intellij-idea
神奇夜光杯17 分钟前
Python酷库之旅-第三方库Pandas(181)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
tang138976427 分钟前
Python(包和模块)
开发语言·python
涛涛6号27 分钟前
PageHelper(springboot,mybatis)
java·spring boot·后端
夜雨翦春韭39 分钟前
【代码随想录Day58】图论Part09
java·开发语言·数据结构·算法·leetcode·图论
Justinc.1 小时前
Flutter图片控件(七)
开发语言·flutter
豪宇刘1 小时前
Shiro回话管理和加密
java·后端·spring
V+zmm101341 小时前
警务辅助人员管理系统小程序ssm+论文源码调试讲解
java·小程序·毕业设计·mvc·课程设计·1024程序员节