各种锁 五类锁: 乐观锁/悲观锁:是否在修改之前给记录增加排它锁 公平锁/非公平锁:请求锁的时间顺序是否与获得锁的时间顺序一致。一致为公平锁,不一致为非公平锁 独占锁/共享锁:是否可以被多个线程共同持有,可以则为共享锁,不可以则为独占锁 可重入锁:一个线程再次获取它自己已经获取的锁时是否会被阻塞 自旋锁:无法获取锁时是否立刻阻塞,还是继续尝试获取指定次数
1. 乐观锁与悲观锁
- 乐观锁和悲观锁的概念来自于数据库
- 悲观锁对数据被修改持悲观态度,认为数据很容易就会被其他线程修改,所以在处理数据之前先加锁,处理完毕释放锁。
- 乐观锁对数据被修改持乐观态度,认为数据一般情况下不会被其他线程修改,所以在处理数据之前不会加锁,而是在数据进行更新时进行冲突检测。
- 对于数据库的悲观锁就是排它锁,在处理数据之前,先尝试给记录加排它锁,如果成功则继续处理,如果失败则挂起或抛出异常,直到数据处理完毕释放锁。
对于数据库的乐观锁所典型的就是CAS方式更新, 例如:update name=‘kevin’ where id=1 and name=‘kevin0’,在更新数据的时候校验这个值是否发生了变化,类似于CAS的操作。
2. 公平锁与非公平锁
据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁,最早请求锁的线程将最早获取到锁。而非公平锁则先请求不一定先得。JUC中的ReentrantLock提供了公平和非公平锁特性。·
- 公平锁:ReentrantLockpairLock= new ReentrantLock(true)
- 非公平锁:ReentrantLockpairLock= new ReentrantLock(false),如果构造函数不传递参数,则默认是非公平锁。
-
3. 独占锁与共享锁
只能被单个线程所持有的锁是独占锁,可以被多个线程持有的锁是共享锁。
- ReentrantLock就是以独占方式实现的,属于悲观锁
- ReadWriteLock读写锁是以共享锁方式实现的,属于乐观锁
- StampedLock的写锁,属于悲观锁。
-
4. 可重入锁
当一个线程想要获取本线程已经持有的锁时,不会被阻塞,而是能够再次获得这个锁,这就是重入锁。
- Synchornized是一种可重入锁,内部维护一个线程标志(谁持有锁),以及一个计数器。
- ReentrantLock也是一种可重入锁
ReadWriteLock、StampedLock的读锁也是可重入锁
5. 自旋锁
当获取锁的时候如果发现锁已经被其他线程占有,则不阻塞自己,也不释放CPU使用权,而是尝试多次获取,如果尝试了指定次数之后仍然没有获得锁,再阻塞线程。
- 自旋锁认为锁不会被长时间持有,使用CPU时间来换取线程上下文切换的开销,从而提高性能。但是可能会浪费CPU资源。
- -XX:PreBlockSpin=n可以设置自旋次数(已经成为了历史),在Jdk7u40时被删除了,其实在jkd6的时候就已经无效了,现在HotSpotVM采用的是adaptive spinning(自适应自旋),虚拟机会根据情况来对每个线程使用不同的自旋次数。
