偏向锁/轻量级锁/重量级锁

这三种锁特指 synchronized 锁的状态,通过在对象头中的 mark word 来表明锁的状态

  • 偏向锁:偏向锁是指当有一个线程获取锁的时候,它就将这个线程记录下来,以后如果尝试获取锁的线程正是偏向锁的拥有者,就可以直接获得锁。
  • 轻量级锁:轻量级锁是指当锁原来是偏向锁的时候,被另一个线程访问,说明存在竞争,那么偏向锁就会升级为轻量级锁,线程会通过自旋的形式尝试获取锁,而不会陷入阻塞。
  • 重量级锁:重量级锁会让其他申请却拿不到锁的线程进入阻塞状态。

可重入锁/非可重入锁

  • 可重入锁指的是线程当前已经持有这把锁了,能在不释放这把锁的情况下,再次获取这把锁

  • 不可重入锁指的是虽然线程当前持有了这把锁,但是如果想再次获取这把锁,也必须要先释放锁后才能再次尝试获取。

共享锁/独占锁

  • 共享锁(读锁)指的是我们同一把锁可以被多个线程同时获得

  • 独占锁(写锁)指的就是,这把锁只能同时被一个线程获得。

公平锁/非公平锁

公平锁就是线程现在拿不到这把锁,那么线程就都会进入等待,开始排队

非公平锁它会在一定情况下,忽略掉已经在排队的线程,发生插队现象。

优点:

  • 公平锁:各个线程公平平等,每个线程都有执行的机会

  • 非公平锁:整体执行速度更快,吞吐量更大

缺点:

  • 公平锁:整体执行速度更慢,吞吐量更小

  • 非公平锁:可能产生线程饥饿问题

悲观锁/乐观锁

  • 悲观锁是在获取资源之前,必须先拿到锁。

  • 乐观锁并不要求在获取资源前拿到锁,也不会锁住资源,通过判断数据是否被改变来进行操作。

例子:

  1. 悲观锁:synchronized关键字和 Lock 接口

  2. 乐观锁:原子类

  3. 大喜大悲:数据库

自旋锁/非自旋锁

自旋锁是如果线程现在拿不到锁,并不直接陷入阻塞或者释放 CPU 资源,而是开始利用循环,不停地尝试获取锁

非自旋锁如果拿不到锁就直接放弃,或者进行其他的处理逻辑,例如去排队、陷入阻塞等。

自旋锁好处:使用循环不停的去尝试获取锁,让线程处于Runnable状态,节省了线程状态切换开销

自旋锁坏处:由于不停的循环尝试获取锁,时间长了会导致大量CPU资源的浪费

可中断锁/不可中断锁

  • 在 Java 中synchronized 关键字修饰的锁代表的是不可中断锁,一旦线程申请了锁,只能等到拿到锁以后才能进行其他的逻辑处理

  • ReentrantLock是一种典型的可中断锁,例如使用lockInterruptibly方法在获取锁的过程中,突然不想获取了,那么也可以在中断之后去做其他的事情

自适应的自旋锁

自适应自旋锁会根据最近自旋获取某一把锁的事件来决定是否省略自旋,或者继续使用自旋

锁消除

如果编译器能确定一个对象只会在一个线程内被使用,就代表肯定是线程安全的,那么编辑器就会吧对应的synchronized给消除,省去加锁解锁的操作。

锁粗化

把多个同步代码块合并称为一个较大的同步代码块,这样可以减少申请锁和释放锁,减少了开销