JUC 之 基础理论

——基础概念

  • synchronized

并发(concurrent)

  • 是在同一实体上的多个事件
  • 是在一台处理器上“同时”处理多个任务
  • 同一时刻,其实是只有一个事件在发生

并行(parallel)

  • 是在不同实体上的多个事件
  • 是在多台处理器上同时处理多个任务
  • 同一时刻,大家各自做各自的事情

进程

  • 简单的说,在系统中运行的一个应用程序就是一个进程,每一个进程都有它自己的内存空间和系统资源

线程

  • 也被称为轻量级进程,在同一个进程内会有1个或多个线程,是大多数操作系统进行时序调度的基本单元

管程

  • Monitor(监视器),也就是锁
    • Monitor 其实是一种同步机制,他的义务是保证同一时间只有一个线程可以访问被保护的数据和代码
    • JVM 中同步是基于进入和退出监视器对象来实现的,每个对象实例都会有一个 Monitor 对象
    • Monitor 对象会和 Java 对象一同创建并销毁,底层是由 C++ 实现
  • 是一种程序结构,结构内的多个子程序行程的多个工作线程互斥访问共享资源
  • JVM 第 3 版:“执行线程要求先成功持有管程,然后才能执行方法,最后当方法完成时释放管程。、执行线程持有了管程,其他任何线程都无法再获取到同一个管程”
  • 在 HotSpot 虚拟机中,monitor 采用 ObjectMonitor 实现
  • 每个对象天生都带着一个对象监视器,每一个被锁住的对象都会和 Monitor 关联起来

ObjectMonitor 关键属性(objectMonitor.hpp)

  • _owner :指向持有 ObjectMonitor 对象的线程
  • _WaitSet :存放处于 wait 状态的线程队列
  • _EntryList :存放处于等待锁 block 状态的线程队列
  • _recursions :锁的重入次数
  • _count :用来记录该线程获取锁的次数

用户线程(User Thread)

  • 是系统的工作线程,它会完成这个程序需要完成的业务操作
  • 一般情况下不做特别说明配置,默认都是用户线程

守护线程(Daemon Thread)

  • 是一种特殊的线程,为其他线程服务的,在后台默默地完成一些系统性的服务,比如垃圾回收线程就是经典例子
  • 作为一个服务线程,没有服务对象就没有必要继续运行了,如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统就可以退出了。所以当系统只剩下守护线程的时候,Java 虚拟机会自动退出
  • Thread.setDaemon(true) 可以手动设置为守护线程(需要在 start() 之前设置,否则报异常)

——锁

悲观锁

  • 认为自己在使用数据的时候,一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改
  • synchronized 关键字和 Lock 的实现类都是悲观锁
  • 适合写操作多的场景,先加锁可以保证写操作时数据正确
  • 显示的锁定之后再操作同步资源

乐观锁

  • 认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁
  • 在 Java 中是通过使用无所编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据;如果这个数据没有被更新,当前线程将自己修改的数据成功写入;如果这个数据已经被其他线程更新,则根据不同的实现方式执行不同的操作,如放弃修改、重试抢锁
  • 判断规则
    • 版本号机制 Version
    • 最常采用的是 CAS(Compare-and-Swap,比较并交换) 算法,Java 原子类(Atomic)中的递增操作就是通过 CAS 自旋实现的
  • 适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升
  • 乐观锁则直接操作同步资源,是一种无锁算法

Synchronized

体现(对象锁 / 类锁)

  • 作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁
  • 作用于代码块,对括号里配置的对象加锁
  • 作用于静态方法,当前类加锁,进入同步代码前要获得当前类对象的锁

字节码分析

  • javap -c test.class 可进行反编译
  • 通常情况下,会出现 1 个 monitorenter 和 2 个 monitorexit 指令
    • 2 个 monitorexit 指令是为了保证异常情况下锁的释放
  • 调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。如果设置了,执行线程会将先持有 monitor 锁,然后再执行方法,最后在方法完成时释放 monitor
  • ACC_STATIC / ACC_SYNCHRONIZED 访问标志区分 该方法是否静态同步方法

可重入的实现机制

  • 每个锁对象拥有一个锁计数器和一个指向持有该锁的线程指针
  • 当执行 Monitorenter 时,如果目标锁对象的计数器为 0,那么说明它没有被其他线程所持有,Java 虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加 1
  • 在目标锁对象的计数器不为 0 的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加 1,苟泽需要等待,直到持有线程释放该锁
  • 当执行 monitorexit 时,Java 虚拟机则需要将锁对象的计数器减 1,。计数器为 0 代表锁已经被释放

公平锁

  • 是指多个线程按照申请锁的顺序来获取锁,类似排队买票,先来先得
  • Lock lock = new ReentrantLock(true) 表示公平锁

非公平锁

  • 是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级反转或者饥饿的状态(某个线程一直得不到锁)
  • Lock lock = new ReentrantLock(false) 表示非公平锁,后来的也可能获得锁
  • Lock lock = new ReentrantLock() 默认为非公平锁

为什么有公平/非公平锁?为什么默认非公平锁?

  • 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从 CPU 的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分的利用 CPU 的时间片,尽量减少 CPU 空闲状态时间
  • 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当 1 个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销

什么时候用公平 / 非公平?

  • 如果为了更高的吞吐量,很明显非公平锁是比较合适的,因为节省了很多线程切换时间,吞吐量自然就上去;否则用公平锁,公平争抢

可重入锁

  • 又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁的对象是同一个),不会因为之前已经获取过还没释放而阻塞
  • Java 中 ReentrantLock 和 synchronized 都是可重入锁,可重入锁的一个优点是可一定程度避免死锁
  • 一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入
  • 自己可以获取自己的内部锁

种类

  • 隐式锁:即 synchronized 关键字使用的锁,默认都是可重入锁;在外层使用锁之后,在内层仍然可以使用,并且不发生死锁;
    • 在一个 synchronized 修饰的方法或代码块的内部调用本类的其他 synchronized 修饰的方法或代码块时,是永远可以得到锁的
  • 显式锁:即 Lock 也有 ReentrantLock 这样的可重入锁
    • 正常情况下,加锁几次就要解锁几次
    • 加锁次数和释放次数不一样,其他线程始终无法获取到锁,一直等待

死锁

  • 是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能得到满足,死锁的可能性就很低,否则就会因争夺有限的资源而陷入死锁
  • 主要原因
    • 系统资源不足
    • 进程运行推进的顺序不合适
    • 资源分配不当

排查

  • 纯命令:
    • jps -l :查出进程号
    • jstack 进程号 :查看进程对应的堆栈信息
  • 图形化:jconsole
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序少年不秃头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值