1.悲观锁
很悲观。每次进入临界区都要加锁,其他线程阻塞等。
在java中,synchronized重量级锁就是悲观锁。
2.乐观锁
很乐观。访问临界区数据的时候不会加锁,数据更新的时候会检测数据是否被其他线程修改过,若被修改过,就再次尝试读取数据,直到没被修改过,最后更新数据。
一般采用版本号机制,会先读取数据的版本号,更新数据的时候比较版本号,版本号一致则更新数据。不一致,则再次读取,直到一致。
java中的乐观锁一般基于CAS自旋实现。synchronized
轻量级锁属于乐观锁,基于AQS实现。
3.公平锁
保证各个线程按照顺序获取锁。可以保证执行顺序。
4.非公平锁
每个线程获取锁的顺序是不确定的。不能保证执行顺序。java中的ReentrantLock默认为非公平锁,可在构造方法中传入true来创建公平锁对象。
5.独占锁
也称排他锁,无论是读操作还是写操作,只能有一个线程能获取临界资源,其他线程阻塞等待。采用的是悲观锁机制。
在java中,ReentrantLock是独占锁。
6.共享锁
允许多个读线程同时获取临界区资源。采用的是乐观锁机制。
在java中,ReentrantReadWriteLock可以实现读/写锁分离,多个读操作可同时获取读锁。
7.可重入锁
也称递归锁。一个线程可以多次占用同一个锁,但正在解锁时,需要执行相同次数的解锁操作。
在java中,ReentrantLock锁就是一个可重入锁。
8.不可重入锁
一个线程不能多次占用同一个锁。只有先释放锁,才能再次获取锁。
9.可中断锁
锁被其他线程获取后,某个线程在阻塞等待过程中,可能由于阻塞时间过长而中断在阻塞等待状态,去执行其他任务。
在java中,ReentrantLock是一个可中断锁。
10.不可中断锁
如果占有锁的线程一直不释放锁,其他想获取锁的线程只能一直阻塞等待。
在java中,synchronized是一种不可中断锁。
11.读/写锁
写锁具有排他性,读锁具有共享性。同一时刻,一个读/写锁只允许有一个写线程,可以有多个读线程。
在java中,ReadWriteLock就是一种读写锁。
12.自旋锁
某个线程没有获取到锁时,不会立即进入阻塞等待状态,而是不断尝试获取锁,直到占用锁的线程释放锁。
自旋锁可能引起死锁和占用CPU时间过长问题。
使用自旋锁注意事项:
(1)程序在占有自旋锁时不能调用自己,也不能递归调用相同的自旋锁。
(2)可以给自旋锁设定 一个循环时间和循环次数。
在java中,CAS是一种自旋锁。
13.死锁
指两个或者多个线程互相持有对方所需要的资源,导致多个线程互相等待,无法继续执行后续任务的现象。
死锁产生的四个必要条件:互斥,不可剥夺,请求与保持和循环等待。
14.饥饿
指一个或多个线程由于一直无法获得需要的资源而无法继续执行的现象。
导致饥饿问题的原因有以下两点:
(1)资源被高优先级的线程抢占,导致低优先级的线程无法获取资源。
(2)某个线程一直不释放资源,导致其他线程无法获取资源。
解决方法:
(1)在程序运行过程中尽量公平地分配资源,可以尝试使用公平锁。
(2)为程序分配充足的系统资源。
(4)尽量避免持有锁的线程长时间占有锁。
15.活锁
指两个或多个线程在同时抢占同一资源时,主动将资源让给其他线程使用,导致这个资源在多个线程间反复横跳,这些线程因无法获得所有资源而无法继续执行的现象。
可以让每个线程随机等待一小段时间再尝试抢占资源,可以大大减少冲突次数,有效避免活锁的发生。