1、可重入锁
对于可重入的理解:
class MyClass {
public synchronized void method1() {
method2();
}
public synchronized void method2() {
}
}
上述代码中,假如线程A执行到了method1,此时线程A获取了这个对象的锁,method1会调用同样由synchronized修饰的method2,此时线程不必重新申请锁,可以直接执行方法method2。如果synchronized不具备可重入性,此时线程A需要重新申请锁。由于线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。
ReentrantLock和synchronized锁都是可重入锁。
2、可中断锁
线程B等待线程A执行锁中的代码,如果线程B可以中断等待线程A去执行其他代码,这种就是可中断锁。
Lock是可中断锁,synchronized不是可中断锁。
3、公平锁
尽量以请求锁的顺序来获取锁,最先请求的的线程获得锁,就是公平锁。
无法保证锁的获取是按照申请的顺序,就是非公平锁。这样会导致某一个或某几个线程永远获取不到锁。
synchronized是非公平锁,ReentrantLock和ReentrantReadWriteLock默认非公平锁,但可以设置为公平锁。ReentrantReadWriteLock并未实现Lock接口,它实现的是ReadWriteLock接口。
4、读写锁
读写锁将对一个资源的访问分成了两个锁,读锁和写锁。ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。
可以通过readLock()获取读锁,通过writeLock()获取写锁。
5、自旋锁
普通锁:线程A在获得普通锁后,如果再有线程B试图获取锁,那么这个线程B将会挂起(阻塞)
自旋锁:线程A在获得普通锁后,线程B可以不放弃CPU时间片,而是在“原地”忙等,直到锁的持有者释放了该锁,是一种非阻塞锁。
有两个问题:
1.过多占据CPU时间
2.死锁问题:有一个线程连续两次试图获得自旋锁(比如在递归程序中),第一次这个线程获得了该锁,当第二次试图加锁的时候,检测到锁已被占用(其实是被自己占用),那么这时,线程会一直等待自己释放该锁,而不能继续执行,这样就引起了死锁。因此递归程序使用自旋锁应该遵循以下原则:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。
摘抄和简化自https://blog.youkuaiyun.com/sunp823/article/details/49886051和https://www.cnblogs.com/aspirant/p/6930436.html