Java 读写锁

作者: 超越爱迪生 | 来源:发表于2016-05-03 15:01 被阅读767次

    对于一个读写锁来说,同一时刻,可以有多个线程拿到读锁,只有一个线程拿到写锁。一旦一个线程拿到写锁,他们任何想要获取读锁或者写锁的线程,都必须等待。

    考虑下面这种情况

    Thread1: A.readlock -> ... (已经拿到读锁)
    Thread2: A.readlock->... (Thread1拿到读锁之后,Thread2也去请求读锁)
    

    很简单,这种情况Thread2也可以顺利拿到读锁,没有任何问题。

    如果这时候有个Thread3,他在Thread1拿到读锁之后,Thread2请求读锁之前,去请求写锁。

    Thread1: A.readlock -> ... (已经拿到读锁)
    Thread3: A.writelock->...(请求写锁)
    Thread2: A.readlock->... 
    

    那么这种情况下,Thread2和Thread3会继续往下执行么?
    Thread3显然是要等待的。Thread2呢?答案是:不一定。

    这要取决于读写锁的实现方法。

    linux内核的rwlock是读写锁的最简单的参考实现。它用一个整数counter代表一个rwlock。0代表没有人占有锁,-1代表有一个线程持有着写锁, 正整数n代表有n个线程持有读锁。要拿读锁时,如果counter小于0, 则继续循环测试,直到counter非负。然后给counter加1,拿锁成功。(当然,得保证“在counter非负的情况下加1”这个操作的原子性,一般通过spinlock或者bit spinlock实现)。可见,如果已经有一个线程拿着读锁还未释放,另一个线程获取读锁会立即成功。

    这个实现很简单,但是存在公平性的问题:写者可能会被饿死。 如果有很多线程相续拿到读锁然后释放读锁,保持counter的值始终大于0,那写者就一直拿不到写锁。http://lwn.net/Articles/364583/

    一个办法是在rwlock元数据中增加一个标记,代表是否有写者在等待读者。读者要拿读锁时,先要等待这个标记的清除。笔者曾经在嵌入式环境中,使用和修改过这样的读写锁。更加先进的方法,是让等待者排一个FIFO队列,比较著名的是MCS lock和CLH lock。

    Java的ReentrantReadWriteLock,就是基于CLH算法。
    正是由于这个排队算法,由于Thread2在Thread3之前,因此Thread2必须等Thread3拿到锁,做完事情,并且释放,才能获得读锁。

    下面是一个简单的实验的代码

    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class Main {
    
        public static void main(String[] args) {
            final ReentrantReadWriteLock.ReadLock readLock;
            final ReentrantReadWriteLock.WriteLock writeLock;
            ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
            readLock = lock.readLock();
            writeLock = lock.writeLock();
    
            System.out.println("main: before readLock.lock()");
            readLock.lock();
            System.out.println("main: after readLock.lock()");
    
            Thread tw = new Thread() {
                @Override
                public void run() {
                    System.out.println("tw: before writeLock.lock()");
                    writeLock.lock();
                    System.out.println("tw: after writeLock.lock()");
                }
            };
    
            Thread tr = new Thread() {
                @Override
                public void run() {
                    System.out.println("tr: before readLock.lock()");
                    readLock.lock();
                    System.out.println("tr: after readLock.lock()");
                }
            };
    
            try {
                tw.start();
                Thread.sleep(1000);
                tr.start();
                tw.join();
                tr.join();
            } catch (InterruptedException ie) {
            }
        }
    }
    

    相关文章

      网友评论

      本文标题:Java 读写锁

      本文链接:https://www.haomeiwen.com/subject/dazlrttx.html