并发编程带来的挑战
强烈推介IDEA2020.2破解激活,IntelliJ IDEA 注册码,2020.2 IDEA 激活码
一、 简述锁升级的流程和原理 为了防止多线程访问共享资源造成线程阻塞的问题,并不会立马对共享资源加重量级锁,使线程进入BLOCKING状态,而是先尝试加偏向锁,在一步步向轻量级锁、重量级锁膨胀的策略,如下图所示: image.png **偏向锁:**不存在资源竞争,资源总是由一个线程获取的情况下使用。在对象头存储了当前线程的id. **轻量级锁:**如果偏向锁被关闭或者已经被其他线程获取,这种情况下抢占同步锁会膨胀到轻量级锁。轻量级锁会通过CAS操作(自旋),把锁对象的标记字段替换为一个指针指向当前线程栈帧中的LockRecord **重量级锁:**多个线程获取同一个锁的时候,虚拟机就会阻塞未获取到锁的线程,并在目标锁释放的时候唤醒阻塞的线程。对象头存储了指向重量级锁的指针。 总结: **偏向锁:**无实际竞争,且将来只有第一个申请锁的线程会使用锁。 **轻量级锁:**无实际竞争,多个线程交替使用锁;允许短时间的锁竞争。 **重量级锁:**有实际竞争,且锁竞争时间长。
二、Synchronized的原理 synchronized有三种方式来加锁,分别是:方法锁,对象锁synchronized(this),类锁synchronized(Demo.Class)。其中在方法锁层面可以有如下3种方式:
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁 静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 使用synchronized加锁会从低到高逐步升级, 无锁->偏向锁->轻量级锁->重量级锁
重量级锁通过对象内部的监视器(monitor)实现,一段被synchronized修饰的同步方法或者代码块时,该线程得先获取到synchronized修饰的对象对应的monitor,过程如下 image.png
三、线程是不是越多越好?为什么? 不是,由于硬件资源的限制,线程过多会造成线程阻塞,阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。
四、wait和notify为什么要加锁 wait方法的语义有两个,一个是释放当前的对象锁、另一个是使得当前线程进入阻塞队列, 而这些操作都和监视器是相关的,所以wait必须要获得一个监视器锁。 而对于notify来说也是一样,它是唤醒一个线程,既然要去唤醒,首先得知道它在哪里?所以就必须要找到这个对象获取到这个对象的锁,然后到这个对象的等待队列中去唤醒一个线程。
作者:https://gper.club/articles/7e7e7f7ff3g5bgc0g69