之前提到的锁基本都是排他锁,这些锁阿紫同一时刻只允许一个线程仅从访问,而ReentrantReadWriteLcok在同一时刻允许多个读线程访问,但是在写线程访问时,所有读线程和其他线程均会被阻塞。读写锁维护了一对锁,一个读锁和一个写锁。通过分离读锁和写锁,使得并发性相比一般的排他锁有很大提升。
Java并发包中提供的读写锁就是ReentrantReadWriteLock,他具有以下特性:
- 公平性选择:支持公平和非公平的锁获取方式
- 支持冲入:同一线程获取读锁之后能再次获取;同意线程获取写锁之后能再次获取写锁,并且还能获取读锁。
- 锁降级:按照获取写锁,再获取读锁,再释放写锁的顺序,写锁就能降级为读锁。
读写锁的接口与示例
ReentrantReadWriteLock时接口ReadWriteLock的具体实现,该接口仅定义了两个方法:
- readLock():获取读锁
- WriteLock():获取写锁
其次还提供哦那个了查看内部状态的方法:
int getReadLcokCount():返回当前读锁被获取次数
int getReadHoldCount:返回当前线程获取读锁次数
Boolean isWriteLocked():判断写锁是否被获取
int getWriteHoldCount():返回放前写锁备货区次数
实现
读写锁状态设计
读写锁依然时组合了一个自定义的同步器(AQS)来实现同步功能,而读写状态就是其同步器的状态,同步状态state是一个int类型的整数。对于读写锁来说,必须将32位的int变量进行切割,其中高16位维护读锁,低16位维护写锁。
比如0000 0000 0000 0010 0000 0000 0000 0011
表示一个线程获取了写锁,并且重入两次写锁(总共三次),并且其获取了读锁,并重入一次读锁(总共两次)。
注意:如果一个线程想要同时拥有读锁和写锁,那么必须先获取写锁才能获取读锁。
写锁的获取与释放
获取:写锁是一个支持重入的皮塔索,如果当前线程已经获取了写锁,则增加写状态,如果当前线程在获取锁时,读锁或者写锁已经被获取,且该线程不是已经获取读锁或者写锁的进程,那么该进程进入等待状态。
释放:写锁每次释放则写状态减一,当写状态减为零时表示真正释放写锁。
读锁的获取与释放
获取:读锁时支持重入的共享锁,,他能被多个线程获取,在没有其他线程持有写锁时,读锁总能被成功获取。而获取所作的知识线程安全的增加读状态。若当前线程在获取读锁时,写锁已被其他线程获取,则该线程进入等待状态。
上文中getReadHoldCount()方法,作用是返回当前线程获取读锁的次数,但是读状态是维护所有线程获取锁次数的总和,因此每个线程各自获取读锁的次数只能保存在ThreadLocal中,由线程自身维护。
释放:和写锁一样,读锁每次释放则读状态减一,当读状态减为零时表示真正释读锁。
ReentrantReadWriteLock仅支持锁降级,也就是写锁降级位读锁,而不支持锁升级,也就是读锁升级为写锁。