QReadWriteLock从名字看就知道是读写锁的意思。和QMutex一样,QReadWriteLock也是线程同步的一种工具。那么它有什么用呢?和QMutex又有什么区别呢?写个例子瞧一瞧。

特点:

包含一个 ReadLock 和 一个 WriteLock 对象

读锁与读锁不互斥;读锁与写锁,写锁与写锁互斥

适合对共享资源有读和写操作,写操作很少,读操作频繁的场景

可以从写锁降级到读锁。获取写锁->获取读锁->释放写锁

无法从读锁升级到写锁

读写锁支持中断

写锁支持Condition;读锁不支持Condition

示例1--根据 key 获取 value 值

private ReadWriteLock lock = new ReentrantReadWriteLock();//定义读写锁

//根据 key 获取 value 值

public Object getValue(String key){

//使用读写锁的基本结构

lock.readLock().lock();//加读锁

Object value = null;

try{

value = cache.get(key);

if(value == null){

lock.readLock().unlock();//value值为空,释放读锁

lock.writeLock().lock();//加写锁,写入value值

try{

//重新检查 value值是否已经被其他线程写入

if(value == null){

value = "value";//写入数据

}

}finally{

lock.writeLock().unlock();

}

lock.readLock().lock();

}

}finally{

lock.readLock().unlock();

}

return value;

}

示例2--多线程环境下的读写锁使用

package constxiong.interview;

import java.util.HashMap;

import java.util.Map;

import java.util.Random;

import java.util.concurrent.locks.ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**

* 测试可重入 读写锁

* @author ConstXiong

* @date 2019-06-10 11:19:42

*/

public class TestReentrantReadWriteLock {

private Map<String, Object> map = new HashMap<String, Object>();

private ReadWriteLock lock = new ReentrantReadWriteLock();

/**

* 根据 key 获取 value

* @param key

* @return

*/

public Object get(String key) {

Object value = null;

lock.readLock().lock();

try {

Thread.sleep(50L);

value = map.get(key);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.readLock().unlock();

}

return value;

}

/**

* 设置key-value

* @param key

* @return

*/

public void set(String key, Object value) {

lock.writeLock().lock();

try {

Thread.sleep(50L);

map.put(key, value);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.writeLock().unlock();

}

}

//测试5个线程读数据,5个线程写数据

public static void main(String[] args) {

final TestReentrantReadWriteLock test = new TestReentrantReadWriteLock();

final String key = "lock";

final Random r = new Random();

for (int i = 0; i < 5; i++) {

new Thread(){

@Override

public void run() {

for (int j = 0; j < 10; j++) {

System.out.println(Thread.currentThread().getName() + " read value=" + test.get(key));

}

}

}.start();

new Thread(){

@Override

public void run() {

for (int j = 0; j < 10; j++) {

int value = r.nextInt(1000);

test.set(key, value);

System.out.println(Thread.currentThread().getName() + " write value=" + value);

}

}

}.start();

}

}

}

公平性

非公平策略

当以非公平初始化时,读锁和写锁的获取的顺序是不确定的。非公平锁主张竞争获取,可能会延缓一个或多个读或写线程,但是会比公平锁有更高的吞吐量。

公平策略

当以公平模式初始化时,线程将会以队列的顺序获取锁。当当前线程释放锁后,等待时间最长的写锁线程就会被分配写锁;或者有一组读线程组等待时间比写线程长,那么这组读线程组将会被分配读锁。

可重入性

默认是可重入的但是要成对的获取锁和释放锁,如下的这段例子,主线程两次获取锁lock.writeLock().lock(),说明其实可重入的,但是输出结果是只有realse one once;因此thread1执行的时候尝试获取锁此时因为主线程未释放,也就导致了死锁。

public static void main(String[] args) throws InterruptedException {

final ReentrantReadWriteLock  lock = new ReentrantReadWriteLock ();

Thread thread1 = new Thread(new Runnable() {

@Override

public void run() {

lock.writeLock().lock();

System.out.println("Thread real execute");

lock.writeLock().unlock();

}

});

lock.writeLock().lock();

lock.writeLock().lock();

thread1.start();

Thread.sleep(200);

System.out.println("realse one once");

lock.writeLock().unlock();

}

在写例子前,先看看要用到的函数:lockForRead、lockForWrite和unlock。比QMutex的例子多一个,从名字上可以看得出来是把lock分为了readlock和writelock。unlock和QMutex里的是一样的,有lock就要unlock。

例子3:

1.从QThread派生一个类ReadThread,重写run函数。

其中gRwLock是一个QReadWriteLock类型的全局对象,run函数的作用就是简单的打印三句话。

2.创建两个ReadThread的对象并start。

看看运行结果:

两个线程还是交替运行的。这是什么情况?我的锁怎么没用呢?........没用就对了,QReadWriteLock是允许并行读的,当调用写锁时其他的lock就要等待。是时候让lockForWrite出场了。

3.再从QThread派生一个类WriteThread,外汇返佣http://www.fx61.com/重写run函数。

WriteThread的run函数里调用的是lockForWrite,锁住的也是三个qDebug。在刚才的基础上再创建一个WriteThread的对象看看运行效果。

运行结果可能会有差异,但是write的三句话始终会按顺序执行。这时候锁才发挥了它的作用。

总结:读写锁允许并行的读,如果遇到写锁时其它锁被锁住。写锁的优先级要高于读锁,如等待的锁中有读锁和写锁时,一旦上一个锁被解锁时会优先执行写锁。QReadWriteLock相对于QMutex的好处是当要保护的对象在大多数的情况是读操作偶尔写操作时,不会造成不必要的堵塞。

最新文章

  1. AWS的SysOps认证考试样题解析
  2. Effective C++ -----条款08: 别让异常逃离析构函数
  3. MFC 线程使用
  4. 批量导入Excel存在的问题及解决方案
  5. SplendidCRM 如何添加及使用中文语言包
  6. 虚拟化之vmware-网络
  7. lintcode: 有效的括号序列
  8. 十三、C# 事件
  9. web前端开发常用工具
  10. JAVA 线程学习 - Thread了解
  11. 和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧。因为,很多PCI的例子都是对S5933,就连微软出版的《Programming the Microsoft Windows Driver Model》都提供了一个完整的S5933的例子。 在这篇有关DDK的开发论文里。
  12. WebGL 在 OpenGL ES 指令 iOS 在 C 分歧版指令分析
  13. JAVA源码剖析(容器篇)HashMap解析(JDK7)
  14. OS.path部分函数的介绍
  15. log4j将日志文件输出到相对路径
  16. mybatis generator自动生成代码时 只生成了insert 而没有其他的
  17. 【转】 诡异的ORA-02289: sequence does not exist
  18. POJ 2455 Secret Milking Machine 【二分】+【最大流】
  19. ora-24550 signo=6 signo=11解决
  20. PAT 1011 A+B和C (15)(C++&JAVA&Python)

热门文章

  1. 无法启动此程序 ,因为计算机中丢失MSVCP120.dll
  2. 让所有Excel数据格全部乘 某个数
  3. JSON.parse 解析json字符串时,遇字符串换行符,解析失败
  4. HTTP、HTTPS等常用服务的默认端口号
  5. mongodb的学习 (1)
  6. 将中国标准时间)转化为yyyy-MM-dd
  7. js获取地址栏的参数
  8. spring事件监听(eventListener)
  9. NOIP模拟测试29(A)
  10. Shell基础(六):使用awk提取文本、awk处理条件、awk综合脚本应用、awk流程控制、awk扩展应用