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类获取读锁和写锁.为防止过多读取所造成的的写线程阻塞,所以在进行写入前都会首先判断读锁的状态.并利用转换方法实现了读锁与写锁的转换.这样就解决了读/写锁的缺陷.
