1、stampeLock的原理
(1)读锁并不会阻塞写锁:当写的时候读失败了,再重新读。
(2)分为乐观锁和悲观锁,乐观锁时读写不互斥,悲观锁读写互斥。
(3)悲观锁不互斥的原理:获取读写锁的时候都会返回一个标识,然后在 读得时候使用validate(long)方法来判断是否发生了写操作,如果发生 了就再次进行读取。
(4)StampedLock的等待队列与RRW的CLH队列相比,有以下特点:
1、当入队一个线程时,如果队尾是读结点,不会直接链接到队尾,而是链接到该读结点的cowait链中,cowait链本质是一个栈;
2、当入队一个线程时,如果队尾是写结点,则直接链接到队尾;
3、唤醒线程的规则和AQS类似,都是首先唤醒队首结点。区别是StampedLock中,当唤醒的结点是读结点时,会唤醒该读结点的cowait链中的所有读结点(顺序和入栈顺序相反,也就是后进先出)。
4、另外,StampedLock使用时要特别小心,避免锁重入的操作,在使用乐观读锁时也需要遵循相应的调用模板,防止出现数据不一致的问题。
5、详情:https://segmentfault.com/a/1190000015808032?utm_source=tag-newest
2、代码实现demo:
package cn.shiyujun.thread.t3;
import java.util.concurrent.locks.StampedLock;
public class Demo {
private int balance;
private StampedLock lock = new StampedLock();
/**
* 在满足一定条件下将读锁转换成写锁
* @param value
*/
public void condditionReadWrite(int value){
//首先判断balance的值是否符合更新的条件
long stamp = lock.readLock();
while (balance > 0){
long writeStamp = lock.tryConvertToWriteLock(stamp);
if(writeStamp != 0){ //成功转换成为写锁
stamp = writeStamp;
balance += value;
break;
}else{
//没有转换成写锁,这里需要首先释放读锁,然后再拿到写锁
lock.unlockRead(stamp);
//获取写锁
stamp = lock.writeLock();
}
}
lock.unlock(stamp);
}
/**
* 乐观锁采用的读操作,读写操作时不互斥
*/
public void optimisticRead(){
long stamp = lock.tryOptimisticRead();
int c = balance;
//这里可能会出现了写操作,因此要进行判断,如果此时发生了写操作重新则重新读取
if(!lock.validate(stamp)){
//要从新读取
long readStamp = lock.readLock();
c = balance;
stamp = readStamp;
lock.unlockRead(stamp);
}
System.out.println("读操作成功,value:"+c);
}
/**
* 悲观锁读,读写互斥,获取读锁失败后
* 队尾结点如果是写结点,读结点会直接链接到队尾;如果队尾是读结点,则会链接到队尾读结点的cowait链中
*/
public void read(){
long stamp = lock.readLock();
int c = balance;
//...
lock.unlockRead(stamp);
System.out.println("读操作成功,balance:"+balance);
}
/**
* 悲观锁写,读写互斥,获取写锁失败后将写锁加入到链接的队尾
* @param value
*/
public void write(int value){
long stamp = lock.writeLock();
balance += value;
lock.unlockWrite(stamp);
System.out.println("写操作成功,balance:"+balance);
}
public static void main(String[] args) {
Demo demo = new Demo();
demo.write(2);
new Thread(new Runnable() {
@Override
public void run() {
demo.write(2);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
demo.optimisticRead();
}
}).start();
}
}