美文网首页Java Concurrency
锁 - 独占 vs 共享

锁 - 独占 vs 共享

作者: 面向对象架构 | 来源:发表于2022-11-24 00:15 被阅读0次

独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
对于Synchronized而言,是独享锁。

独占锁

典型的独占锁:synchronized、vector、hashtable、ReentrantReadWriteLock中的写锁

该锁每一次只能被一个线程所持有。

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {

    /** Inner class providing readlock */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;

    /** Performs all synchronization mechanics */
    final Sync sync;

    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }


    /**
     * The lock returned by method {@link ReentrantReadWriteLock#writeLock}.
     */
    public static class WriteLock implements Lock, java.io.Serializable {
        private final Sync sync;

        protected WriteLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

        public void lock() {
            // 这里是重点
            sync.acquire(1);
        }
    }

    public static class ReadLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -5992448646407690164L;
        private final Sync sync;

        protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

        public void lock() {
            // 这里是重点
            sync.acquireShared(1);
        }
    }

    /**
     * Synchronization implementation for ReentrantReadWriteLock.
     * Subclassed into fair and nonfair versions.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {

        protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
            Thread current = Thread.currentThread();
            // 当前锁个数
            int c = getState();
            // 写锁
            int w = exclusiveCount(c);
            if (c != 0) {
                // c != 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;
        }

        // 因为存在锁降级情况,如果存在写锁且锁的持有者不是当前线程,则直接返回失败,否则继续。
        // 依据公平性原则,调用 readerShouldBlock() 方法来判断读锁是否不需要阻塞,
        //     读锁持有线程数小于最大值(65535),且 CAS 设置锁状态成
        protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             * 1. If write lock held by another thread, fail.
             * 2. Otherwise, this thread is eligible for
             *    lock wrt state, so ask if it should block
             *    because of queue policy. If not, try
             *    to grant by CASing state and updating count.
             *    Note that step does not check for reentrant
             *    acquires, which is postponed to full version
             *    to avoid having to check hold count in
             *    the more typical non-reentrant case.
             * 3. If step 2 fails either because thread
             *    apparently not eligible or CAS fails or count
             *    saturated, chain to version with full retry loop.
             */
            Thread current = Thread.currentThread();
            int c = getState();
            // exclusiveCount(c)计算写锁
            // 如果存在写锁,且锁的持有者不是当前线程,直接返回-1
            // 存在锁降级问题,后续阐述
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            // 读锁
            int r = sharedCount(c);
            // readerShouldBlock():读锁是否需要等待(公平锁原则)
            // r < MAX_COUNT:持有线程小于最大数(65535)
            // compareAndSetState(c, c + SHARED_UNIT):设置读取锁状态
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) { // 修改高16位的状态,所以要加上2^16
                // r==0 说明读本线程是第一个获取读锁的线程
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                // firstReader==current 说明此次获取读锁为firstReader的重入
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    // 判断成功说明当前缓存的HoldCounter不是本线程的
                    if (rh == null ||
                        rh.tid != LockSupport.getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    // rh.count==0 时有可能本线程对应的ThreadLocalMap中已经没有了rh这个键
                    // 为了保险其见需要重新配置一下
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }

    }

}

ReentrantReadWriteLock 有两把锁:ReadLock和WriteLock,可知,一个读锁,一个写锁合称“读写锁”。 ReadLock 和 WriteLock 是靠内部类 Sync 实现的锁。

Sync 是 AQS 的一个子类,这种结构在 CountDownLatch 、ReentrantLock 、Semaphore 里面也都存在。

在ReentrantReadWriteLock 里面,读锁和写锁的锁主体都是 Sync ,但读锁和写锁的加锁方式不一样。
读锁是共享锁,写锁是独享锁

共享锁

典型的共享锁:ReentrantReadWriteLock中的读锁

该锁可被多个线程所持有。

典型的就是ReentrantReadWriteLock里的读锁,它的读锁是可以被共享的,但是它的写锁确每次只能被独占。

对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁

读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。

独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

AQS

抽象队列同步器(AbstractQueuedSynchronizer,简称AQS)是用来构建锁或者其他同步组件的基础框架,它使用一个整型的volatile变量(命名为state)来维护同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。

AQS、非阻塞数据结构和原子变量类等基础类都是基于volatile变量的读/写和CAS实现,而像Lock、同步器、阻塞队列、Executor和并发容器等高层类又是基于基础类实现。

相关文章

  • 锁 - 独占 vs 共享

    独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。对于Synchronized而言,是...

  • 07 番外 基于AQS实现一个锁

    1 独占锁 2 共享锁

  • AQS之独占和共享锁

    AQS独占和共享锁,ReentantLock为独占锁,ReentantReadWriteLock中readLock...

  • Java Semaphore/CountDownLatch/Cy

    前言 前面分析了基于AQS的独占锁ReentrantLock、共享锁/独占锁ReentrantReadWriteL...

  • AQS原理

    分为独占锁和共享锁: 获取独占锁: CAS设置为1,成功则获取锁,否则调用 acquire获取锁 tryAcqui...

  • Java并发编程——ReentrantReadWriteLock

    在AQS的介绍中,锁分为独占锁和共享锁,在上节中我们介绍了独占锁ReentrantLock,本次将针对另一个独占锁...

  • java并发基础-各种锁和ThreadLocal

    概念 独占锁和共享锁 独占锁:一种悲观锁,任何时候都只能有一个线程得到锁共享锁:一种乐观锁,可以由多个线程持有,比...

  • 深入浅出AQS之共享锁模式

    在了解了AQS独占锁模式以后,接下来再来看看共享锁的实现原理。 搞清楚AQS独占锁的实现原理之后,再看共享锁的实现...

  • GO互斥锁、读写锁

    读写锁:读时共享,写时独占。写锁优先级比读锁优先级高 通过mutex实现读时共享,写时独占代码示例: 打印结果: ...

  • redis锁分享

    共享锁(读锁) LOCK IN SHARE MODE 排他锁(写锁,独占锁,互斥锁)FOR UPDATE 乐观锁和...

网友评论

    本文标题:锁 - 独占 vs 共享

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