注册 登录
编程论坛 J2EE论坛

ReadWriteLock 如何使用?

专注的蝴蝶 发布于 2019-09-17 17:28, 5022 次点击
QReadWriteLock从名字看就知道是读写锁的意思。和QMutex一样,QReadWriteLock也是线程同步的一种工具。那么它有什么用呢?和QMutex又有什么区别呢?写个例子瞧一瞧。
特点:
包含一个 ReadLock 和 一个 WriteLock 对象
读锁与读锁不互斥;读锁与写锁,写锁与写锁互斥
适合对共享资源有读和写操作,写操作很少,读操作频繁的场景
可以从写锁降级到读锁。外汇返佣http://www.获取写锁->获取读锁->释放写锁
无法从读锁升级到写锁
读写锁支持中断
写锁支持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,重写run函数。
WriteThread的run函数里调用的是lockForWrite,锁住的也是三个qDebug。在刚才的基础上再创建一个WriteThread的对象看看运行效果。
运行结果可能会有差异,但是write的三句话始终会按顺序执行。这时候锁才发挥了它的作用。
总结:读写锁允许并行的读,如果遇到写锁时其它锁被锁住。写锁的优先级要高于读锁,如等待的锁中有读锁和写锁时,一旦上一个锁被解锁时会优先执行写锁。QReadWriteLock相对于QMutex的好处是当要保护的对象在大多数的情况是读操作偶尔写操作时,不会造成不必要的堵塞。
0 回复
1