JDK8新增StampedLock对ReentrantReadWriteLock进行增强#
stamped:贴上邮票的; 盖上邮戳的,拿到锁之后会返回给我们一个票据,根据这个Stamp的值判断是在读的时候发生了写,还是在写的时候发生了读操作
解决的问题:
在高并发的情况下,读操作的次数远远大于写操作,,因为读写互斥,写操作可能就会出现饥饿的情况,一直抢占不到cpu的资源
解决方法:
- 当然可以使用公平的ReadWriteLock,但是依然有性能问题
- StampedLock的乐观锁实现了读写共享提升了!
StampedLock里面有两种锁
乐观锁:
读锁并不会阻塞写锁
public long tryOptimisticRead() {...}
悲观锁:
读写互斥,和ReentrantReadWriteLock实现相同的功能
API:
独占的获取写锁,若锁被其他线程获取到,则阻塞,注意它是相对于ReentrantReadWriteLock来讲,它是有返回值的,返回值的作用:
- 释放锁(unlock的需要参数)
- 进行锁的转换
/** * Exclusively acquires the lock, blocking if necessary * until available. * * @return a stamp that can be used to unlock or convert mode */ public long writeLock() { long s, next; // bypass acquireWrite in fully unlocked case only return ((((s = state) & ABITS) == 0L && U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ? next : acquireWrite(false, 0L)); }
//一旦写锁可用立即获取,返回值可以用作释放锁或被锁的转换使用, 0表示没有获取到锁 /** * Exclusively acquires the lock if it is immediately available. * * @return a stamp that can be used to unlock or convert mode, * or zero if the lock is not available */ public long tryWriteLock() { long s, next; return ((((s = state) & ABITS) == 0L && U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ? next : 0L); } //延迟获取锁 /** * Exclusively acquires the lock if it is available within the * given time and the current thread has not been interrupted. * Behavior under timeout and interruption matches that specified * for method {@link Lock#tryLock(long,TimeUnit)}. * * @param time the maximum time to wait for the lock * @param unit the time unit of the {@code time} argument * @return a stamp that can be used to unlock or convert mode, * or zero if the lock is not available * @throws InterruptedException if the current thread is interrupted * before acquiring the lock */ public long tryWriteLock(long time, TimeUnit unit) ... // 非独占的获取读锁 /** * Non-exclusively acquires the lock, blocking if necessary * until available. * * @return a stamp that can be used to unlock or convert mode */ public long readLock() { long s = state, next; // bypass acquireRead on common uncontended case return ((whead == wtail && (s & ABITS) < RFULL && U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ? next : acquireRead(false, 0L)); } // 一旦锁可用,立即非独占的获取读锁 /** * Non-exclusively acquires the lock if it is immediately available. * * @return a stamp that can be used to unlock or convert mode, * or zero if the lock is not available */ public long tryReadLock() { for (;;) { long s, m, next; if ((m = (s = state) & ABITS) == WBIT) return 0L; else if (m < RFULL) { if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) return next; } else if ((next = tryIncReaderOverflow(s)) != 0L) return next; } }
乐观锁!!!它获取到的锁,读写锁非互斥
返回一个标记,这个标记过一会用去 校验, 如果锁是排它锁,返回零
/** * Returns a stamp that can later be validated, or zero * if exclusively locked. * * @return a stamp, or zero if exclusively locked */ public long tryOptimisticRead() { long s; return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L; }
校验,如果锁还没被任何线程获取,获取被持有当前stamp的线程获取返回true , 如果 stamp为0,返回false
/** * Returns true if the lock has not been exclusively acquired * since issuance of the given stamp. Always returns false if the * stamp is zero. Always returns true if the stamp represents a * currently held lock. Invoking this method with a value not * obtained from {@link #tryOptimisticRead} or a locking method * for this lock has no defined effect or result. * * @param stamp a stamp * @return {@code true} if the lock has not been exclusively acquired * since issuance of the given stamp; else false */ public boolean validate(long stamp) { U.loadFence(); return (stamp & SBITS) == (state & SBITS); }
锁的释放
/** * If the lock state matches the given stamp, releases the * exclusive lock. * * @param stamp a stamp returned by a write-lock operation * @throws IllegalMonitorStateException if the stamp does * not match the current state of this lock */ public void unlockWrite(long stamp) { WNode h; if (state != stamp || (stamp & WBIT) == 0L) throw new IllegalMonitorStateException(); state = (stamp += WBIT) == 0L ? ORIGIN : stamp; if ((h = whead) != null && h.status != 0) release(h); } //释放任意匹配成功的锁 /** * If the lock state matches the given stamp, releases the * corresponding mode of the lock. * * @param stamp a stamp returned by a lock operation * @throws IllegalMonitorStateException if the stamp does * not match the current state of this lock */ public void unlock(long stamp) {
读,写 锁之间的转换
public long tryConvertToWriteLock(long stamp) { ..} public long tryConvertToWriteLock(long stamp) { ..} //释放读锁 public void unlockRead(long stamp) {..} //释放写锁 public void unlockWrite(long stamp) {..}
简单使用后#
/* * StampedLock的简单使用 * */ public class StampedLock01 { private int balance; StampedLock stamptedLock = new StampedLock(); //悲观读 public void read(){ long s = stamptedLock.readLock(); try{ try { System.out.println("拿到读锁"+Thread.currentThread().getName()+System.currentTimeMillis()); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } }finally { stamptedLock.unlockRead(s); } } // 乐观锁 public void OptimismRead(){ //获取乐观锁,拿到 标记 long stm = stamptedLock.tryOptimisticRead(); try { System.out.println("一开始读取到的balance=="+balance); //方便测试 睡一会 Thread.sleep(200); // 在读的时候,可能会出现现写操作-- 判断 if(!stamptedLock.validate(stm)){ //1 锁空闲可用 2 拥有当前stm的线程获取到锁,返回true 其他返回false //重新读取 long l = stamptedLock.readLock(); System.out.println("乐观锁中发现了在读时,发现写操作,重新读结果为: "+balance); // 更新标记, 用于锁的释放 stm=l; } }catch (Exception e){ e.printStackTrace(); }finally { //释放锁 stamptedLock.unlockRead(stm); } } // 带条件的读写锁 public void MyConditionReadWriteLock(int v){ //判断balance是否符合更新的条件 long stm = stamptedLock.readLock(); try{ // 为什么不会if 而用while while(stm>0){ //转换成写锁; long s1 = stamptedLock.tryConvertToWriteLock(stm); if (s1!=0){// 成功转换 balance+=v; System.out.println("进行写操作: balance=="+balance+99); stm=s1; break; }else{ //没有转换成功 //释放读锁 stamptedLock.unlockRead(stm); // 获取写 long s2 = stamptedLock.writeLock(); stm=s2; System.out.println("手动获取写锁..."); } } }finally { stamptedLock.unlock(stm); } } //独占的写 public void write(int v){ long stamp = stamptedLock.writeLock(); try{ balance+=v; System.out.println("进行写的操作...结果: "+balance+" "+Thread.currentThread().getName()+" "+"write中的标记为:"+stamp); Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } finally { stamptedLock.unlockWrite(stamp); } } public static void main(String[] args) { StampedLock01 stampedLock01 = new StampedLock01(); //测试普通的read // 测试成功 异步执行 //测试独占的写 成功 /* new Thread(()->{ // 乐观读 stampedLock01.OptimismRead(); }).start(); new Thread(()->{ stampedLock01.write(3); }).start(); */ new Thread(()->{ stampedLock01.MyConditionReadWriteLock(1); }).start(); } }
最后提一下 > 如何选择大名鼎鼎的AQS最有名的实现类ReentrantLock和synchronized这个JVM提供的锁,一开始synchronized是一个笨重的重量级锁,但是jdk1.5之后,进行了偏向锁,轻量级锁的优化,使它的性能和ReentrantLock擦不多了,于是没有特殊的要求,官方推荐使用synchronized
什么情况下使用ReentrantLock呢?
- 使用它特有的公平锁
- 使用它的Condition类,分组唤醒指定的线程
- 提供了能够中断正在等待锁的线程的机制,lock.lockInterrupted()