读/写锁可以保证并发访问下的数据写入安全与读取性能,但是在读线程非常多的情况下,有可能造成写线程的长时间阻塞,从而减少写线程的调度次数。为此JUC中针对读/写锁提出了改进方案,提供了无障碍锁(StampedLock),使用这种锁的特点在于:若干个读线程彼此之间不会互相影响,但是依然可以保证多个写线程的独占操作。
在StampedLock中分为:
- 写
- 读
- 乐观读
3种模式,以提高并发处理性能,同时也可以实现锁类型的转换。
1、使用StampedLock实现银行账户并发操作的例子:
package com.mydemo;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;
public class JUCDemo {
public static void main(String[] args) {
// 实例化对象-存放数据
Account account = new Account("张三", 0.0);
double moneyData[] = new double[]{100.00, 200.00, 300.00, 400.00, 500.00};
// 5个写入线程
for(int i = 0; i < 5; i++){
new Thread(
()->{
for(int j = 0; j < moneyData.length; j++){
// 存放数据
account.saveMoney(moneyData[j]);
}
}, "写线程:"
).start();
}
// 30个读取线程
for(int j = 0; j < 30; j++){
new Thread(
()->{
while (true){
// 获取数据
System.out.println(account.toString());
}
}, "读线程:"
).start();
}
}
}
// 银行账户
class Account{
private String name; // 账户名称
private double asset; // 账户资产
// 读/写锁
private StampedLock stampedLock = new StampedLock();
/**
* 双参构造函数---设置账户信息
* @param name
* @param asset
*/
public Account(String name, double asset) {
this.name = name;
this.asset = asset;
}
/**
* 资产追加
* @param money
*/
public void saveMoney(double money){
// 获取读锁,检查状态
long stamp = this.stampedLock.readLock();
boolean flag = true; // 设置标志位
try {
// 转为写锁
long writeStamp = this.stampedLock.tryConvertToWriteLock(stamp);
while(flag){
// 当前为写锁
if(writeStamp != 0){
stamp = writeStamp; // 修改为写锁的标记
this.asset += money; // 进行资产修改
TimeUnit.SECONDS.sleep(1); // 模拟延迟
System.out.println("【" + Thread.currentThread().getName() +
"】修改银行资产数据,修改金额:" + money +
",当前资产:" + this.asset);
// 结束循环
flag = false;
}else{
// 释放读锁
this.stampedLock.unlock(stamp);
// 获取写锁
writeStamp = this.stampedLock.writeLock();
stamp = writeStamp;
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 解锁
this.stampedLock.unlock(stamp);
}
}
/**
* 数据读取
* @return
*/
public String toString(){
// 获取乐观锁
long stamp = this.stampedLock.tryOptimisticRead();
try {
double current = this.asset; // 获取当前的资产
TimeUnit.SECONDS.sleep(1); // 模拟延迟
/**
* validate()方法虽然可以检测,但是依然有可能出现异常,
* 所以本出依据StampledLock类的源代码多追加了一个验证机制
*/
if(!this.stampedLock.validate(stamp) || (stamp & (long)(Math.pow(2,7)-1)) == 0){
long readStamp = this.stampedLock.readLock();
current = this.asset;
stamp = readStamp;
}
return "【账户信息:(" + Thread.currentThread().getName() +
")】账户名称:" + this.name +
"、银行资产:" + this.asset;
}catch (Exception e){
return null;
}finally {
try {
this.stampedLock.unlock(stamp);
}catch (Exception e){}
}
}
}