一、 简述锁升级的流程和原理
为了防止多线程访问共享资源造成线程阻塞的问题,并不会立马对共享资源加重量级锁,使线程进入BLOCKING状态,而是先尝试加偏向锁,在一步步向轻量级锁、重量级锁膨胀的策略,如下图所示:
image.png
**偏向锁:**不存在资源竞争,资源总是由一个线程获取的情况下使用。在对象头存储了当前线程的id.
**轻量级锁:**如果偏向锁被关闭或者已经被其他线程获取,这种情况下抢占同步锁会膨胀到轻量级锁。轻量级锁会通过CAS操作(自旋),把锁对象的标记字段替换为一个指针指向当前线程栈帧中的LockRecord
**重量级锁:**多个线程获取同一个锁的时候,虚拟机就会阻塞未获取到锁的线程,并在目标锁释放的时候唤醒阻塞的线程。对象头存储了指向重量级锁的指针。
总结:
**偏向锁:**无实际竞争,且将来只有第一个申请锁的线程会使用锁。
**轻量级锁:**无实际竞争,多个线程交替使用锁;允许短时间的锁竞争。
**重量级锁:**有实际竞争,且锁竞争时间长。
二、Synchronized的原理
synchronized有三种方式来加锁,分别是:方法锁,对象锁synchronized(this),类锁synchronized(Demo.Class)。其中在方法锁层面可以有如下3种方式:
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
使用synchronized加锁会从低到高逐步升级, 无锁->偏向锁->轻量级锁->重量级锁
重量级锁通过对象内部的监视器(monitor)实现,一段被synchronized修饰的同步方法或者代码块时,该线程得先获取到sync
hronized修饰的对象对应的monitor,过程如下
image.png
三、线程是不是越多越好?为什么?
不是,由于硬件资源的限制,线程过多会造成线程阻塞,阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。
四、wait和notify为什么要加锁
wait方法的语义有两个,一个是释放当前的对象锁、另一个是使得当前线程进入阻塞队列, 而这些操作都和监视器是相关的,所以wait必须要获得一个监视器锁。
而对于notify来说也是一样,它是唤醒一个线程,既然要去唤醒,首先得知道它在哪里?所以就必须要找到这个对象获取到这个对象的锁,然后到这个对象的等待队列中去唤醒一个线程。
作者:https://gper.club/articles/7e7e7f7ff3g5bgc0g69
最后
小编这些年深知大多数初中级工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Java全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你需要这些资料,⬅专栏获取
录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**
如果你需要这些资料,⬅专栏获取