StampedLock java1.8提供的, 性能比ReadWriteLock好.
乐观锁
乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低
(不是没有, 所以还要加锁, 区别于不加锁的乐观读),每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号
,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。
java中的乐观锁
基本都是通过CAS
操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。
悲观锁
悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高
,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会block直到拿到锁。java中的悲观锁就是Synchronized
final StampedLock sl = new StampedLock();
/**
* 悲观读锁
* 与ReadWriteLock的读锁相似
* 允许多个线程进行读操作
* 与写锁互斥
**/
//获取悲观锁
long stamp = sl.readLock();
try {
//。。。。
}finally {
sl.unlockRead(stamp);
}
/**
* 写锁
* 与ReadWriteLock的写锁相似
* 只允许一个线程进行写操作
* 与读锁互斥
**/
//获取写锁
long stamp1 = sl.writeLock();
try{
//。。。
}finally {
//释放写锁
sl.unlockWrite(stamp1);
}
/**
* 乐观读锁升级为悲观读
* 锁的严苛程度变强叫做升级,反之叫做降级
**/
//获取乐观锁
long stamp = sl.tryOptimisticRead();
//判断执行读操作期间,是否存在写操作,如果存在则sl.validate返回false
if (!sl.validate(stamp)){
//升级为悲观锁
stamp = sl.readLock();
try{
//。。。
}finally {
//释放悲观读锁
sl.unlockRead(stamp);
}
语法
final StampedLock sl = new StampedLock();
/**
* 悲观读锁
* 与ReadWriteLock的读锁相似
* 允许多个线程进行读操作
* 与写锁互斥
**/
//获取悲观锁
long stamp = sl.readLock();
try {
//。。。。
}finally {
sl.unlockRead(stamp);
}
/**
* 写锁
* 与ReadWriteLock的写锁相似
* 只允许一个线程进行写操作
* 与读锁互斥
**/
//获取写锁
long stamp1 = sl.writeLock();
try{
//。。。
}finally {
//释放写锁
sl.unlockWrite(stamp1);
}
/**
* 乐观读锁升级为悲观读
* 锁的严苛程度变强叫做升级,反之叫做降级
**/
//获取乐观锁
long stamp = sl.tryOptimisticRead();
//判断执行读操作期间,是否存在写操作,如果存在则sl.validate返回false
if (!sl.validate(stamp)){
//升级为悲观锁
stamp = sl.readLock();
try{
//。。。
}finally {
//释放悲观读锁
sl.unlockRead(stamp);
}
StampedLock提供的乐观读,是允许一个线程获取写锁的,也就是说不是所有的写操作都是被阻塞的。
乐观读和乐观锁是不一样的,乐观读这个操作是无锁的,乐观读认为读的时候不会有写的操作。
示例
import java.util.concurrent.locks.StampedLock;
/**
* 悲观锁乐观锁
**/
public class Point {
private int x;
private int y;
final StampedLock sl = new StampedLock();
//计算到原点的距离
int distanceFromOrigin() throws Exception{
//乐观锁
long stamp = sl.tryOptimisticRead();
//读入局部变量,读的过程数据可能被修改
int curX = x;
int curY = y;
//判断执行读操作期间,是否存在写操作,如果存在则sl.validate返回false
if (!sl.validate(stamp)){
//升级为悲观锁
stamp = sl.readLock();
try{
curX = x;
curY = y;
}finally {
//释放悲观读锁
sl.unlockRead(stamp);
}
}
return (int)Math.sqrt(curX*curX + curY*curY);
}
}
StampedLock
支持三种模式:写锁
、悲观读
和乐观读
。- 允许多个线程同时获取乐观锁和悲观读锁。
- 只允许一个线程获取写锁,
写锁
和悲观读锁
是互斥
的。 - 使用StampedLock一定不要调用中断操作,如果需要支持中断功能,一定使用
可中断的悲观读锁
readLockInterruptibly()
和写锁writeLockInterruptibly()
。 - StampedLock里的
写锁
和悲观读锁
加锁成功之后,都会返回一个stamp
;然后解锁
的时候,需要传入这个stamp
。 - StampedLock
不支持重入
(ReadWriteLock支持)。 - StampedLock的悲观读锁、写锁都不支持条件变量。
- StampedLock支持锁的降级(通过tryConvertToReadLock()方法实现)和升级(通过 tryConvertToWriteLock()方法实现),但是建议你要慎重使用。
数据库中的乐观锁
- 在表中添加一个数值型
版本号
字段version。 - 每次更新表时都将version字段加1。
- 修改数据时使用主键和version作为修改条件,
version一致
则会修改成功
。 - 如果修改失败,通过
主键查询获取最新数据
再执行修改。