一、乐观锁

乐观锁,顾名思义,就是比较乐观的锁,当需要操作到共享数据时,它就认为没有其它的线程在操作该数据,态度比较乐观,乐观锁操作数据时不会上锁,在更新的时候会判断一下在此期间是否有其他线程去更新这个数据。乐观锁可以使用版本号机制和CAS算法实现。在 Java 语言中
java.util.concurrent.atomic包下的原子类就是使用CAS 乐观锁实现的。适合读多写少的情况。

二、悲观锁

悲观锁就是和乐观锁截然不同的态度,当有一个线程操作共享数据时,它就会认为有其他线程也要和它做同样的事情,不管三七二十一,索性就加锁。一个共享数据加了悲观锁,那线程每次想操作这个数据前都会假设其他线程也可能会操作这个数据,所以每次操作前都会上锁,这样其他线程想操作这个数据拿不到锁只能阻塞了。在 Java 语言中 synchronized 和 ReentrantLock等就是典型的悲观锁,还有一些使用了 synchronized 关键字的容器类如 HashTable 等也是悲观锁的应用。适合写多读少的情况。

三、独占锁

独占锁是指锁一次只能被一个线程所持有。如果一个线程对数据加上独占锁后,那么其他线程不能再对该数据加任何类型的锁。获得独占锁的线程即能读数据又能修改数据。JDK中的synchronized和java.util.concurrent(JUC)包中Lock的实现类就是独占锁。独占锁也叫排它锁。这里就要提到互斥锁了,互斥锁是独占锁的一种常规实现,是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。

四、共享锁

共享锁是指锁可被多个线程所持有。如果一个线程对数据加上共享锁后,那么其他线程只能对数据再加共享锁,不能加独占锁。获得共享锁的线程只能读数据,不能修改数据。在 JDK 中 ReentrantReadWriteLock 就是一种共享锁。而读写锁是共享锁的一种具体实现。读写锁管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,而写锁是独占的。写锁的优先级要高于读锁,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容。读写锁相比于互斥锁并发程度更高,每次只有一个写线程,但是同时可以有多个线程并发读。

五、公平锁

公平锁顾名思义,就是对于每一个线程都是公平的,会按照申请锁的顺序来获取锁。俗话说:世界里没有绝对的公平,大家觉得公平锁是绝对的公平吗?

六、非公平锁

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转,或者饥饿的状态(某个线程一直得不到锁)。

七、可重入锁

可重入锁又称之为递归锁,是指同一个线程在外层方法获取了锁,在进入内层方法(内层方法也加了锁)会自动获取锁。

八、自旋锁

自旋锁是指线程在没有获得锁时不是被直接挂起,而是执行一个等待循环,这个等待循环就是所谓的自旋。自旋锁的目的是为了减少线程被挂起的几率,因为线程的挂起和唤醒也都是耗资源的操作。如果锁被另一个线程占用的时间比较长,即使自旋了之后当前线程还是会被挂起,等待循环就会变成浪费系统资源的操作,反而降低了整体性能。因此自旋锁是不适应锁占用时间长的并发情况的。在JDK1.6又引入了自适应自旋,这个就比较智能了,自旋时间不再固定,由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定。如果虚拟机认为这次自旋也很有可能再次成功那就会持续较多的时间,如果自旋很少成功,那以后可能就直接省略掉自旋过程,避免浪费处理器资源。

----------------------------------以下是锁升级相关概念---------------------------------------------------------

九、无锁

无锁状态其实就是上面讲的乐观锁。

十、偏向锁

Java偏向锁(Biased Locking)是指它会偏向于第一个访问锁的线程,如果在运行过程中,只有一个线程访问加锁的资源,不存在多线程竞争的情况,那么线程是不需要重复获取锁的,这种情况下,就会给线程加一个偏向锁。偏向锁的实现是通过控制对象Mark Word的标志位来实现的,如果当前是可偏向状态,需要进一步判断对象头存储的线程 ID 是否与当前线程 ID 一致,如果一致直接进入。

十、轻量级锁

当线程竞争变得比较激烈时,偏向锁就会升级为轻量级锁,轻量级锁认为虽然竞争是存在的,但是理想情况下竞争的程度很低,通过自旋方式等待上一个线程释放锁。

十一、重量级锁

如果线程并发进一步加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,又来了其他线程访问时,轻量级锁就会膨胀为重量级锁,重量级锁会使除了此时拥有锁的线程以外的线程都阻塞。升级到重量级锁其实就是互斥锁了,一个线程拿到锁,其余线程都会处于阻塞等待状态。在 Java 中,synchronized 关键字内部实现原理就是锁升级的过程:无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁。

还有就是锁优化技术,包括锁粗化和锁消除,锁粗化就是将多个同步块的数量减少,并将单个同步块的作用范围扩大,本质上就是将多次上锁、解锁的请求合并为一次同步请求。锁消除是指虚拟机编译器在运行时检测到了共享数据没有竞争的锁,从而将这些锁进行消除。