九又四分之三站台

0%

锁的分类

Java中有各式各样的锁,大致可以分为以下几类

  • 公平锁 / 非公平锁
  • 可重入锁
  • 互斥锁 / 共享锁
  • 乐观锁 / 悲观锁
  • 分段锁
  • 偏向锁 / 轻量级锁 / 重量级锁
  • 自旋锁

公平锁 / 非公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁。 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。 对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。 对于Synchronized而言,也是一种非公平锁。

可重入锁

可重入锁又叫递归锁, 同一个线程在外层方法获取锁时,进入内层方法自动获取锁不会被阻塞
ReentrantLock和Synchronized都是可重入锁

以下例子,如果synchronized不是可重入锁的话就有可能造成死锁

1
2
3
4
5
6
7
8
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}

synchronized void setB() throws Exception{
Thread.sleep(1000);
}

互斥锁 / 共享锁

  • 互斥锁:X锁,只能被一个线程持有,写锁是互斥锁
  • 共享锁:S锁,可以被多个线程持有,读锁是共享锁

乐观锁 / 悲观锁

乐观锁和悲观锁并不是具体指什么类型的锁,而是对待并发同步的方式

  • 悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。
  • 乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。

悲观锁在Java中是用各种具体的锁来实现的,乐观锁是无锁,采用CAS算法实现,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

分段锁

分段锁不是一种具体的锁,而是一种锁的设计,通过细化锁的粒度提高性能

Java7中的ConcurrentHashMap就采用了分段锁的设计,当进行put操作时,先通过hashcode找到要插入的分段中,然后对该分段进行加锁。当多个线程进行put操作时,只要不是在同一个分段中就能执行并行插入

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

  • 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
  • 轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
  • 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

自旋锁

尝试获取锁的线程不会阻塞而是采用循环的方式获取锁,可以减少线程切换的切换但是会消耗CPU