读写锁介绍现实中有这样一种场景:对共享资源有读写操作,写操作没有读操作频繁(阅读更多和少写)。没有写操作的时候,多个线程同时读取一个资源是没有问题的,所以应该允许多个线程同时读取共享资源(读取和读取可以并发);但是如果一个线程要写这些共享资源,就不应该允许其他线程读写这个资源(读和写,写和读,写和写是互斥的)。在读多于写的情况下,读写锁可以提供比独占锁更好的并发性和吞吐量。针对这种场景,JAVA的并发包提供了一个读写锁ReentrantReadWriteLock,它维护着一对相关的锁,一个用于只读操作,称为读锁;一种用于写操作,称为写锁,描述如下:线程进入读锁的前提条件:没有写请求或写请求没有其他线程的写锁,只有调用线程和持有线程锁是一样的。一个线程进入写锁的前提条件:没有其他线程的读锁没有其他线程的写锁和读写锁有以下三个重要特性:公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量数量或不公平优于公平。可重入:读锁和写锁都支持线程可重入。以读写线程为例:读线程获取读锁后,可以再次获取读锁。写线程获取写锁后,可以再次获取写锁,也可以获取读锁。锁降级:按照获取写锁,获取读锁,最后释放写锁的顺序,可以将写锁降级为读锁。看完上面的描述,你可能有点晕,那我就举一个之前的开发订单的例子,帮助大家理解。我们的订单有主订单和子订单的概念:主订单的代码是orderCode,子订单的代码是subOrderCode。对应关系为1:N。我在退款的时候,需要支持子订单,退主订单。子订单退款,维度为subOrderCode主订单退款,维度为orderCode可能会出现并发,我们可以在orderCode上加一个读写锁如果是主订单退款的话,子订单退款对不对?如果互斥是子订单退款,其实是可以并行的,但是子订单是subOrderCode维度,需要加一个subOrderCode的互斥量。读写锁同时使用如何存储读写锁,可以通过state的值来存储。高16位表示读锁,低16位表示写锁。例如:0000000000000000(1<<16)0000000000000000高16位不为0:有读锁c>>>16低16位不为0:有写锁5ReadWriteLock接口我们可以看到ReentranReadWriteLock有两个A锁,一个读锁,一个写锁。缓存操作示例:publicclassReentrantReadWriteLockCacheTest{staticMapmap=newHashMap();静态ReentrantReadWriteLockrwl=newReentrantReadWriteLock();静态锁r=rwl.readLock();静态锁w=rwl.writeLock();//获取某个key对应的值publicstaticfinalObjectget(Stringkey){r.lock();尝试{返回map.get(key);}最后{r.unlock();}}//设置key对应的值,返回旧值publicstaticfinalObjectput(Stringkey,Objectvalue){w.lock();尝试{returnmap.put(key,value);}最后{w.unlock();}}//清除所有内容publicstaticfinalvoidclear(){w.lock();尝试{map.clear();}最后{w.unlock();}}}在上面的例子中,Cache结合了一个非线程安全的HashMap作为缓存的实现,使用读写锁的读锁和写锁来保证Cache是??线程安全的。在读操作get(Stringkey)方法中,需要获取读锁,防止并发访问该方法被阻塞。写操作put(Stringkey,Objectvalue)方法和clear()方法在更新HashMap时必须提前获取写锁。获得写锁后,其他线程无法获得读锁和写锁,只有写锁被阻塞。释放锁后,其他的读写操作可以继续进行。Cache使用读写锁提高了读操作的并发性,同时也保证了每次写操作对所有读写操作都是可见的,同时简化了编程方式。锁降级锁降级是指将写锁降级为读锁。如果当前线程持有写锁,然后释放,最后获取读锁,这个分段的过程不能称为锁降级。锁降级是指持有一个(当前拥有的)写锁,获取一个读锁,然后释放一个(以前拥有的)写锁的过程。锁降级可以帮助我们在不被其他线程破坏的情况下获取到当前线程的修改结果,防止更新丢失。锁降级示例因为数据不经常变化,所以可以多个线程并发处理数据。当数据发生变化时,如果当前线程感知到数据变化,就会准备数据,而其他处理线程则被阻塞,直到当前线程完成数据准备。privatefinalReentrantReadWriteLockrwl=newReentrantReadWriteLock();privatefinalLockreadLock=rwl.readLock();privatefinalLockwriteLock=rwl.writeLock();privatevolatilebooleanupdate=false;publicvoidprocessData(lock){)readLock。;if(!update){//读锁必须先释放readLock.unlock();//锁从获取写锁降级到启动writeLock.lock();try{if(!update){//TODO数据准备过程(略)update=true;}readLock.lock();}最后{writeLock.unlock();}//锁降级完成,写锁降级为读锁}try{//TODO使用数据的过程(略)}finally{readLock.unlock();}}注意:读锁不支持条件变量重入,不升级不支持:持有读锁时,获取写锁会导致永久等待重入,支持降级:如果持有写lock,可以获得读锁。ReentranReadWriteLock结构方法结构设计读写状态设计源码分析写锁锁方法tryAcquire是写锁保护的锁核心逻辑finalbooleantryAcquire(intacquires){/**演练:*1\.如果读取计数非零或写入计数非零*和自己r是一个不同的线程,失败。*2\。如果计数会饱和,则失败。(这只能*在计数已经非零时发生。)*3\.否则,如果*它是可重入获取或*队列策略允许,则此线程有资格获得锁定。如果是这样,更新状态*并设置所有者。*/当前线程=Thread.currentThread();intc=getState();//获取写锁状态intw=exclusiveCount(c);if(c!=0){//(注意:如果c!=0且w==0则共享计数!=0)if(w==0||current!=getExclusiveOwnerThread())returnfalse;if(w+exclusiveCount(acquires)>MAX_COUNT)thrownewError("超过最大锁计数");//重入acquire//重新进入setState(c+acquires);返回真;}//获取写锁if(writerShouldBlock()||!compareAndSetState(c,c+acquires))returnfalse;//设置写锁ownersetExclusiveOwnerTh阅读(当前);returntrue;}写锁的释放protectedfinalbooleantryRelease(intreleases){if(!isHeldExclusively())thrownewIllegalMonitorStateException();intnextc=getState()-发布;booleanfree=exclusiveCount(nextc)==0;如果(免费)setExclusiveOwnerThread(空);设置状态(下一步);returnfree;}读锁的获取protectedfinalinttryAcquireShared(intunused){/**Walkthrough:*1\.如果写锁被另一个线程持有,则失败。*2\。否则,此线程有资格*锁定wrt状态,因此询问它是否应该阻塞*因为队列策略。如果不是,请尝试*通过CASing状态和更新计数来授予。*请注意,步骤不检查可重入*获取,它被推迟到完整版本*以避免在更典型的非可重入情况下检查保持计数。*3\。如果第2步失败是因为线程*显然不符合条件或CAS失败或计数*饱和,链接到具有完整重试循环的版本。*/当前线程=Thread.currentThread();intc=getState();如果(exclusiveCount(c)!=0&&getExclusiveOwnerThread()!=current)返回-1;intr=sharedCount(c);if(!readerShouldBlock()&&r0;}else{if(rh==null){rh=cachedHoldCounter;如果(右==空||rh.tid!=getThreadId(current)){rh=readHolds.get();如果(rh.count==0)readHolds.remove();}}if(rh.count==0)返回-1;}}if(sharedCount(c)==MAX_COUNT)thrownewError("超过最大锁计数");if(compareAndSetState(c,c+SHARED_UNIT)){if(sharedCount(c)==0){firstReader=current;firstReaderHoldCount=1;}elseif(firstReader==current){firstReaderHoldCount++;}else{if(rh==null)rh=cachedHoldCounter;if(rh==null||rh.tid!=getThreadId(current))rh=readHolds.get();elseif(rh.count==0)readHolds.set(rh);rh.count++;cachedHoldCounter=rh;//释放缓存}return1;}}}读锁的释放protectedfinalbooleantryReleaseShared(intunused){Threadcurrent=Thread.currentThread();if(firstReader==current){//断言firstReaderHoldCount>0;如果(firstReaderHoldCount==1)firstReader=null;elsefirstReaderHoldCount--;}else{HoldCounterrh=cachedHoldCounter;if(rh==null||rh.tid!=getThreadId(current))rh=readHolds.get();intcount=rh.count;如果(计数<=1){readHolds.remove();如果(计数<=0)抛出不匹配的解锁异常();}--rh.count;}for(;;){intc=getState();intnextc=c-SHARED_UNIT;if(compareAndSetState(c,nextc))//释放读锁对读者没有影响,//bu如果//读锁和写锁现在都是空闲的,它可能允许等待的写入者继续。返回nextc==0;}}