前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >话说 ReadWriteLock

话说 ReadWriteLock

原创
作者头像
木子的昼夜
修改2021-04-06 11:08:42
3280
修改2021-04-06 11:08:42
举报

ReadWriteLock

读写锁:读读不互斥,读写互斥,写写互斥;

也就是说:

A读的时候B可以读,

A读的时候B不可以写,

A写的时候B不可以写

这里举个例子:不同线程对变量x 读 写

代码语言:txt
复制
public class ReadWriteLockTest {
    ReadWriteLock rw = new ReentrantReadWriteLock();
    public int x = 0;
    public static void main(String[] args) {

    }

    // A读
    public void A(){
        try{
            // 读锁
            rw.readLock().lock();
            System.out.println("A开始读: x="+x);
            sleep(5);
            System.out.println("A读完了: x="+x);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            rw.readLock().unlock();
        }
    }

    // B读
    public void B(){
        try{
            // 读锁
            rw.readLock().lock();
            System.out.println("B开始读: x="+x);
            sleep(5);
            System.out.println("B读完了: x="+x);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            rw.readLock().unlock();
        }
    }

    // C写
    public void C(){
        try{
            // 写锁
            rw.writeLock().lock();
            System.out.println("C开始写: x="+x);
            sleep(5);
            x = 10;
            System.out.println("C写完了: x="+x);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            rw.writeLock().unlock();
        }
    }
     // D写
    public void D(){
        try{
            // 写锁
            rw.writeLock().lock();
            System.out.println("D开始写: x="+x);
            sleep(5);
            x = 100;
            System.out.println("D写完了: x="+x);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            rw.writeLock().unlock();
        }
    }
    // E 同一线程 读写不互斥
    public void E(){
        try{
            // 写锁
            rw.writeLock().lock();
            System.out.println("E开始写: x="+x);
            x = 99;
            rw.readLock().lock();
            System.out.println("E没写完呢 E开始读:x="+x);
            x = 100;
            System.out.println("E写完了: x="+x);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            rw.writeLock().unlock();
        }
    }
    
    // 睡眠指定秒
    public void sleep(int s){
        try {
            Thread.sleep(s*1000);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}
1. A 读 B可读 读读共享
代码语言:txt
复制
 public static void main(String[] args) {
     ReadWriteLockTest test = new ReadWriteLockTest();
     new Thread(test::A).start();
     new Thread(test::B).start();
 }
输出结果:
A开始读: x=0
B开始读: x=0
B读完了: x=0
A读完了: x=0
        
2. A 读 C 不可写 读写互斥
代码语言:txt
复制
public static void main(String[] args) {
    ReadWriteLockTest test = new ReadWriteLockTest();
    new Thread(test::A).start();
    new Thread(test::C).start();
}
输出结果:
A开始读: x=0
A读完了: x=0
C开始写: x=0
C写完了: x=10
3. B 写 A 不可读 读写互斥
代码语言:txt
复制
public static void main(String[] args) {
    ReadWriteLockTest test = new ReadWriteLockTest();
    new Thread(test::C).start();
    new Thread(test::A).start();
}
输出结果:
C开始写: x=0
C写完了: x=10
A开始读: x=10
A读完了: x=10
4. C写 D不可写 写写互斥
代码语言:txt
复制
 public static void main(String[] args) {
        ReadWriteLockTest test = new ReadWriteLockTest();
        new Thread(test::C).start();
        new Thread(test::D).start();
 }
输出结果:
C开始写: x=0
C写完了: x=10
D开始写: x=10
D写完了: x=100
5. 线程自己读写不互斥
代码语言:txt
复制
public static void main(String[] args) {
    ReadWriteLockTest test = new ReadWriteLockTest();
    new Thread(test::E).start();
}
6. 总结

读读共享,读写互斥,写写互斥

可以把读比作是女生,把共享资源比作是厕所,女生跟女生可以拉手进厕所(读读),女生和男生不可以拉手进厕所(读写),男生和男生不可以拉手进厕所(写写)

7. 唠一唠实现方式
7.1 类继承关系
01.png
01.png
02.png
02.png
03.png
03.png
7.2 lock过程
代码语言:txt
复制
ReadWriteLock rw = new ReentrantReadWriteLock();
rw.readLock().lock();

读锁lock大体流程是这样的:

lock01.png
lock01.png

与ReentrantLock获取锁的过程基本一致,只是在tryAcquire(写锁) 与 tryReleaseShared(读锁) 的时候有些区别

前置知识:

读写锁是怎么用state标记是读锁(数量)还是写锁(数量)的 ,要是我设计这个代码,

我可能会用 int readState,int writeState , 两个单独的状态来标识读锁 (数量)和写锁(数量)

但是AQS 说了 只能用 一个state一个双向队列 来 实现 (模板方法),你不能自己瞎给我改。

看看大佬们怎么实现的:

大佬把state切开了,int类型数据大小为4字节 32位 ,大佬把32分成了16+16 高16位表示共享锁,低16位表示独占锁

共享独占锁.png
共享独占锁.png

tryAcquireShared

代码语言:txt
复制
protected final int tryAcquireShared(int unused) {
    // 获取当前线程 
    Thread current = Thread.currentThread();
    // 获取state
    int c = getState();
    // 看一下是不是独占状态(写锁),如果是独占再看一下持有锁的线程是不是当前线程,如果不是返回-1 失败
    // 如果线程E获取了独占锁 他是可以再获取共享锁锁的 看5线程自己读写不互斥例子
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    // 获取共享锁数量(高16位)
    int r = sharedCount(c);
    // 判断是不是需要block
    if (!readerShouldBlock() &&
        // 判断获取锁的线程有没有超过最大线程
        r < MAX_COUNT && 
        // cas设置state 这里不是c+1 是c+SHARED_UNIT 
        // SHARED_UNIT的二进制位: 10000_0000_0000_0000 
        // 为什么是加这个 因为是高16位+1  也就是需要加65536=65535+1
        compareAndSetState(c, c + SHARED_UNIT)) {
        // 到这里其实已经获取锁成功了 下边的一些操作 是设置一些需要的属性
        if (r == 0) {
            // 如果是第一个独占锁 就设置firstReader为当前线程
            // firstReaderHoldCount = 1
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            // 如果第一个独享锁占有者是自己 那就firstReaderHoldCount++
            firstReaderHoldCount++;
        } else {
            // 第一个独占锁不是自己  这里操作骚里骚气 没有很懂
            // 只知道是把持有独占锁的次数+1(排除第一个获取独占锁的线程 因为上边那两个变量单独记录了)
            // 这里用到了threadlocal技术
            
            // cachedHoldCounter 这个玩意存着最后获取共享锁的线程 和 数量 
            HoldCounter rh = cachedHoldCounter;
            // 
            if (rh == null || rh.tid != getThreadId(current))
                // readHolds.get()就是返回ThreadLocal中存储的对象 线程第一次进来会创建
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            // 独占锁总数+1 
            rh.count++;
        }
        return 1;
    }
    // 如果没有成功 调用fullTryAcquireShared
    return fullTryAcquireShared(current);
}

fullTryAcquireShared

代码语言:txt
复制
 
// 上边失败了 这里就死循环获取锁 
final int fullTryAcquireShared(Thread current) {
    
    HoldCounter rh = null;
    for (;;) {
        // 获取state
        int c = getState();
        // 跟上边一样 
        if (exclusiveCount(c) != 0) {
            if (getExclusiveOwnerThread() != current)
                return -1;
            // else we hold the exclusive lock; blocking here
            // would cause deadlock.
            // readerShouldBlock这个公平锁和非公平锁的逻辑不一样 
        } else if (readerShouldBlock()) {
            // Make sure we're not acquiring read lock reentrantly
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
            } else {
                if (rh == null) {
                    rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current)) {
                        rh = readHolds.get();
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                }
                if (rh.count == 0)
                    return -1;
            }
        }
        // 最大获取锁线程数校验
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // +1 
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            // 上边一样 一系列设置
            if (sharedCount(c) == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
                firstReaderHoldCount++;
            } else {
                if (rh == null)
                    rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
                cachedHoldCounter = rh; // cache for release
            }
            return 1;
        }
    }
}
读写锁.png
读写锁.png

就先到这吧 内容确实不少 看的有点蒙

欢迎关注公众号:

公众号二维码.jpg
公众号二维码.jpg

发现更多精彩

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ReadWriteLock
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档