ReentrantReadWriteLock读/写锁可以保证并发访问下的数据写入安全和读取性能,但是在读线程非常多的情况下.有可能造成写线程的长时间阻塞.从而减少写线程的调用次数.为此JUC中针对读/写锁提出了改进方案.提供了无障碍锁StampedLock,使用这种锁的特点在于:若干读线程彼此间不会相互影响.但是依然可以保证多个写线程的独占操作.
StampedlLock中分为3中模式.写.读,乐观锁.以提高并发处理性能.同时也可以 实现类型转换
public class Account {
//账号名称
private String name;
//资产
private double asset;
//读写锁
// private ReadWriteLock lock=new ReentrantReadWriteLock();
private StampedLock lock = new StampedLock();
public Account(String name, double asset) {
this.name = name;
this.asset = asset;
}
/**
* 资产追加
*
* @param money
*/
public void saveMoney(double money) {
//获取读锁.检查状态
long stamp = this.lock.readLock();
boolean flag = true;
try {
//转化为写锁
long writeStamp = this.lock.tryConvertToWriteLock(stamp);
while (flag) {
if (writeStamp != 0) {
stamp = writeStamp;
this.asset += money;
System.out.println(Thread.currentThread().getName() + "修改银行账户数据:" + money + ";当前资产:" + this.asset);
flag = false;
} else {
//没有获取到写锁
//释放读锁
this.lock.unlockRead(stamp);
//获取写锁
stamp = this.lock.writeLock();
}
}
} catch (Exception e) {
} finally {
this.lock.unlock(stamp);
}
}
/**
* 获取资产数据
*
* @return
*/
public String getAsset() {
//获取读锁
long stamp = this.lock.tryOptimisticRead();
try {
double current = this.asset;
//validate()方法虽然可以检查但是依然有可能出现异常.所以本处依据StampledLock类的源码多追加了一个验证机制
if (!this.lock.validate(stamp) || (stamp & (long) (Math.pow(2, 7) - 1)) == 0) {
long readStamp = this.lock.readLock();
current = this.asset;
stamp = readStamp;
}
return "账户信息:" + Thread.currentThread().getName() + ";账户名称:" + this.name + ";总资产:" + asset;
} catch (Exception e) {
return null;
} finally {
//释放指定的写锁
try {
this.lock.unlockRead(stamp);
} catch (Exception e) {
e.printStackTrace();
}
}
}.
public class JUCDemo2 {
public static void main(String[] args) {
Account accout = new Account("QYF", 0.0);
double[] moneys = new double[]{120.0, 100.0, 80.0, 60.0, 40.0, 10.0, 5.0};
//写线程存放资产
for (int i = 0; i < 3; i++) {
new Thread(() -> {
for (int n = 0; n < moneys.length; n++) {
accout.saveMoney(moneys[n]);
}
}).start();
}
//读线程读取数据
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.err.println(accout.getAsset());
}).start();
}
}
}
输出结果:
Thread-0修改银行账户数据:120.0;当前资产:120.0
Thread-0修改银行账户数据:100.0;当前资产:220.0
Thread-0修改银行账户数据:80.0;当前资产:300.0
Thread-0修改银行账户数据:60.0;当前资产:360.0
Thread-0修改银行账户数据:40.0;当前资产:400.0
Thread-0修改银行账户数据:10.0;当前资产:410.0
Thread-0修改银行账户数据:5.0;当前资产:415.0
Thread-1修改银行账户数据:120.0;当前资产:535.0
Thread-1修改银行账户数据:100.0;当前资产:635.0
Thread-1修改银行账户数据:80.0;当前资产:715.0
Thread-1修改银行账户数据:60.0;当前资产:775.0
Thread-1修改银行账户数据:40.0;当前资产:815.0
Thread-1修改银行账户数据:10.0;当前资产:825.0
Thread-1修改银行账户数据:5.0;当前资产:830.0
Thread-2修改银行账户数据:100.0;当前资产:930.0
Thread-2修改银行账户数据:80.0;当前资产:1010.0
Thread-2修改银行账户数据:60.0;当前资产:1070.0
Thread-2修改银行账户数据:40.0;当前资产:1110.0
Thread-2修改银行账户数据:10.0;当前资产:1120.0
Thread-2修改银行账户数据:5.0;当前资产:1125.0
账户信息:Thread-10;账户名称:QYF;总资产:830.0
账户信息:Thread-6;账户名称:QYF;总资产:830.0
账户信息:Thread-5;账户名称:QYF;总资产:830.0
账户信息:Thread-3;账户名称:QYF;总资产:830.0
账户信息:Thread-9;账户名称:QYF;总资产:830.0
账户信息:Thread-12;账户名称:QYF;总资产:1125.0
账户信息:Thread-7;账户名称:QYF;总资产:1125.0
账户信息:Thread-4;账户名称:QYF;总资产:1125.0
账户信息:Thread-11;账户名称:QYF;总资产:1125.0
账户信息:Thread-8;账户名称:QYF;总资产:1125.0
在Account类中利用StampedLock类获取读锁和写锁.为防止过多读取所造成的的写线程阻塞,所以在进行写入前都会首先判断读锁的状态.并利用转换方法实现了读锁与写锁的转换.这样就解决了读/写锁的缺陷.