并发编程--读写锁ReadWriteLock和ReentrantReadWriteLock写锁与读锁(二)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq924862077/article/details/70763794

在上一篇博客并发编程--读写锁ReadWriteLock和ReentrantReadWriteLock(一)中我们简单介绍了一下读写锁的相关知识,接下来来我们介绍一下读锁的实现机制,简单的来说写锁就是一个独占锁,如果看过ReentrantLock相关的知识,应该会对独占锁的实现有一些简单的理解,简单来说独占锁的实现是当锁标识位state为0时,当前线程获取锁,并将state进行加一操作,其他线程来获取锁时会发现state此时不为0,则将当前线程添加到FIFO队列中,并将线程进行阻塞,线程释放锁时会将state进行减一操作,并唤醒FIFO队列中阻塞的线程来竞争锁。

接下来我们看看写锁是如何实现的。 lock的操作是如果获取锁则将锁标识位state进行加一操作。

public void lock() {
            sync.acquire(1);//锁标识位加一
        }

acquire中会尝试获取锁,如果获取锁失败则将线程添加到FIFO队列中挂起

 public final void acquire(int arg) {
		//tryAcquire(arg) 尝试获取锁,如果获取锁失败调用acquireQueued将线程添加FIFO队列中并挂起
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
在tryAcquire中判断是否可以获取锁,这里有一点不同的是如果一个读线程获取了锁,则锁标识位state加上65536
 protected final boolean tryAcquire(int acquires) {
			//获取当前线程
            Thread current = Thread.currentThread();
			//获取锁标识位值
            int c = getState();
			//与65535进行与运算,如果此时读线程获取锁则c=65536,此时w=0
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
				//当此时有写锁或者读锁是都返回false
                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;
        }

如果读线程获取了锁则此时state值为65536,w=exclusiveCount(c)获取的值为0,则返回false,当两个读线程竞争锁时,w=exclusiveCount(c) 值w=c,但current != getExclusiveOwnerThread()为true则返回false。其实写锁是一个独占锁,当state值不为0且运行线程不是当前线程则就无法获取锁。

释放写锁:

public void unlock() {
            sync.release(1);//将state值进行减一操作
        }

release中的操作是释放锁并唤起其他阻塞的线程

public final boolean release(int arg) {
		//释放锁
        if (tryRelease(arg)) {
            Node h = head;
			//唤起其他阻塞线程
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
 protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
			//将state进行减release操作
            int nextc = getState() - releases;
			//当nextc等于0时将当前执行线程设置为null
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
			//修改锁标识位
            setState(nextc);
            return free;
        }
这样就完成了锁的释放。

接下来我们来看看读锁是如何获取锁的,并且读锁是共享锁,我们来了解一下读锁是如何实现共享锁的。

调用lock方法获取锁

 public void lock() {
			//获取锁
            sync.acquireShared(1);
        }
acquireShared会尝试获取锁,如果无法获取锁时则将当前线程添加到FIFO队列中,并将线程阻塞

   public final void acquireShared(int arg) {
		//尝试获取锁
        if (tryAcquireShared(arg) < 0)
			//将线程添加到FIFO队列中
            doAcquireShared(arg);
    }

tryAcquireShared中的操作是如果获取共享锁的话会将state设置为state = state +65536,这样下一个读锁过来的时候state>>>16值一般会小于65535的,这样就实现了读锁不会被读锁阻塞了。exclusiveCount(0)的操作是c&65535,65536&65535等于0则是可以获取读锁,不然就是写锁,阻塞当前阻塞。

protected final int tryAcquireShared(int unused) {
 
            Thread current = Thread.currentThread();
			//获取锁标识位值
            int c = getState();
			//如果是不存在锁exclusiveCount(c)等于0,如果是存在读锁exclusiveCount(c)65536&65535等于0,如果存在写锁exclusiveCount(c)返回值不为0
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
				
            int r = sharedCount(c);
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
				//最终设置锁标识位位state=state+65536,这个是与写锁不同的地方
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }

锁的释放

读锁在设置锁时将state设置为state=state+65536,那样释放锁时state设置为state = state-65536,这样就完成了锁的释放操作。







展开阅读全文

没有更多推荐了,返回首页