Java多线程(5)----浅谈读写锁

1,前言

ReentrantLock是一个排他锁,这种锁在同一时刻只允许一个线程进行访问。在实际生产中,多个线程同时读一个资源是不会产生并发问题的

读写锁在同一时刻可以允许多个线程访问,但是在写线程访问时,所有的读线程和其他写线程均会被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁来提高性能。

讨论读写锁时,会涉及到重入锁

2,读写锁的实现分析

2.1,读写状态设计

读写锁是通过分离读锁与写锁,以实现共享读和独占写

那么同步状态的设计是一个问题。

读写锁使用整型变量来维护多个读线程和一个写线程的状态

读写锁将变量切分成了两个部分,用高16位表示读,低16位表示写

在这里插入图片描述

因为读写锁是一个可重入锁,所以这里的读状态统计实际上是多个线程重复获取读锁的次数总和。而每个线程各自获取读锁的次数只能选择保存在ThreadLocal中,由线程自身维护,这使得获取读锁的实现变得复杂。

读写锁中关于获取状态的相关源代码如下:

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
    
    	......
    
        abstract static class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = 6317671515068378041L;


            static final int SHARED_SHIFT   = 16;
            static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
            static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
            static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

            /** Returns the number of shared holds represented in count  */
            static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
            /** Returns the number of exclusive holds represented in count  */
            static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

            ......
            
        }
    
}

2.2,写锁的获取和释放

写锁是一个支持重进入的排他锁。如果当前线程获取了写锁,则增加了写状态。

如果一个线程试图获取写锁时,发现读锁已经被获取;或者线程试图获取写锁时,发现写锁已经被其他线程获取了;这些情况会导致线程被阻塞。

之所以当获取写锁,而读锁已经被获取时,线程会被阻塞;是因为读写锁要保证写锁的操作对于读锁是可见的。否则就会产生并发问题

相关的源代码如下:

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
    
    	......
    
        abstract static class Sync extends AbstractQueuedSynchronizer {
         
            ......
            
            protected final boolean tryAcquire(int acquires) {

                Thread current = Thread.currentThread();
                int c = getState();
                int w = exclusiveCount(c);
                if (c != 0) {
                    // (Note: if c != 0 and w == 0 then shared count != 0)
                    //这里代表着:当前有线程获取了锁,如果w为0,代表着写锁并未被获取,那么自然是读锁被获取了;如果w不为0,则代表写锁被获取,并且假设当前线程不是获取写锁的线程,获取自然会失败!
                    if (w == 0 || current != getExclusiveOwnerThread())
                        return false;
                    if (w + exclusiveCount(acquires) > MAX_COUNT)
                        throw new Error("Maximum lock count exceeded");
                    // Reentrant acquire
                    setState(c + acquires);
                    return true;
                }
                if (writerShouldBlock() ||
                    !compareAndSetState(c, c + acquires))
                    return false;
                setExclusiveOwnerThread(current);
                return true;
            }
            
            ......
            
        }
    
    	......
    
}

读锁的释放与ReentrantLock的释放过程是一致的,有多少次获取该锁,就有对应次数的释放动作。

2.3,读锁的获取与释放

读锁是一个支持重进入的共享锁,它能够同时被多个线程所持有,在写锁没有被其他线程访问时,读锁的获取往往是能成功地。

        protected final int tryAcquireShared(int unused) {
            
            //当前线程
            Thread current = Thread.currentThread();
            //获取同步状态
            int c = getState();
            //如果写锁已经被获取并且当前线程跟获取写锁的线程不是同一个
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            //获取读的状态值
            int r = sharedCount(c);
            //这一步是成功获取读锁
            if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    //如果此时读锁并未被获取过
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    //如果是持有读锁的线程重复获取该锁
                    firstReaderHoldCount++;
                } else {

                    ......
                    
                }
                return 1;
            }
            return fullTryAcquireShared(current); 
        }
       

在这个方法中,如果其他线程已经获取了写锁,那么当前线程获取读锁时就会失败,并进入等待状态;如果当前线程获取了写锁或者写锁未被获取,则当前线程(线程安全,依靠 CAS 保证)增加读状态,成功获取读锁(获取写锁再然获取读锁,这与锁降级有关)

2.4,锁降级

锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,那么在持有写锁的情况下,获取读锁,获取之后再释放写锁,这个过程就叫锁降级。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值