基于synchronized实现读写锁

什么时候可以获取读锁

  • 读锁重入
  • 非写锁独占状态且阻塞队列为空
    • 非写锁独占意味着可能已有线程获取读锁
    • 阻塞队列为空意味着不存在获取不到写锁的线程被阻塞
  • 由于不满足上述两种条件获取读锁将被阻塞(已有新城获取到写锁或被阻塞),后续被唤醒后将再次尝试获取读锁,如果当前被阻塞的线程所在的节点不存在前继写锁节点线程,则认为可以获取锁

什么情况下可以获取写锁

  • 写锁重入
  • 当前不存在读写锁且阻塞队列为空
  • 阻塞被唤醒后如果不存在任何读写锁且该阻塞节点位于头部时可以获取锁

释放读锁

  • 当且仅当释放最后一把读锁时才会尝试唤醒阻塞队列,实际上因为使用的synchronized,此时队列头一定时被祖寺的获取写锁的节点,其它被阻塞的写节点由于不处于队列头部将无法获取锁,读节点则因为存在前继写节点也无法获取锁

释放写锁

  • 当释放写锁时可能存在连续的排队等待获取读锁的节点,因此使用signalAll唤醒所有阻塞线程时,每个读线程都会判断自己是否存在前继的写节点,如果不存在就可以获取到读锁,否则继续阻塞等待
package jdk.sync;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

import java.util.HashMap;
import java.util.Map;

public class MyReentrantLock {
    private Map<Thread, Integer> readers = new HashMap<>();
    private Thread writer;
    private int cnt;
    private Node head = new Node(), tail;

    @AllArgsConstructor
    @NoArgsConstructor
    static class Node {
        Thread t;
        boolean flag;
        Node prev, next;
    }

    public synchronized void readLock() {
        Node n = null;
        boolean interrupted = false;
        Thread ct = Thread.currentThread();
        for (; ; ) {
            if (readers.computeIfPresent(ct, (k, v) -> ++v) != null) {
                break;
            } else if (writer == null && tail == null) {
                readers.put(ct, 1);
                break;
            } else if (!hasWaiterPredecessor(n) && writer == null) {
                readers.put(ct, 1);
                break;
            } else {
                if (n == null)
                    n = enq(false);
                try {
                    wait();
                } catch (InterruptedException e) {
                    interrupted = true;
                }
            }
        }
        remove(n);
        if (interrupted) {
            ct.interrupt();
        }
    }

    public synchronized void readUnlock() {
        Thread ct = Thread.currentThread();
        Integer cnt = readers.get(ct);
        if (cnt == null) {
            throw new Error("当前线程不持有读锁,不能执行释放操作!");
        } else if (readers.compute(ct, (k, v) -> v == 1 ? null : --v) == null && readers.isEmpty()) {
            notifyAll();
        }
    }

    public synchronized void writeLock() {
        Thread ct = Thread.currentThread();
        boolean interrupted = false;
        Node n = null;
        for (; ; ) {
            if (writer == ct) {
                writer = ct;
                cnt++;
                break;
            } else if (readers.isEmpty() && writer == null && tail == null) {
                writer = ct;
                cnt = 1;
                break;
            } else if (n == head.next && writer == null) {
                writer = ct;
                cnt = 1;
                break;
            } else {
                if (n == null) {
                    n = enq(true);
                }
                try {
                    wait();
                } catch (InterruptedException e) {
                    interrupted = true;
                }
            }
        }
        remove(n);
        if (interrupted) {
            ct.interrupt();
        }
    }

    public synchronized void writeUnlock() {
        if (Thread.currentThread() != writer) {
            throw new Error("当前线程不持有写锁,不能释放写锁");
        } else if (--cnt == 0) {
            writer = null;
            notifyAll();
        }
    }

    private void remove(Node n) {
        if (n != null) {
            if (n == tail) {
                n.prev.next = null;
                tail = n.prev;
            } else {
                n.prev.next = n.next;
                n.next.prev = n.prev;
                n.next = null;
            }
            n.prev = null;
            if (tail == head) {
                tail = null;
            }
        }
    }

    private Node enq(boolean flag) {
        Node n = new Node(Thread.currentThread(), flag, null, null), p;
        if (tail == null) {
            p = head;
            tail = n;
        } else {
            p = tail;
            tail = n;
        }
        p.next = n;
        n.prev = p;
        return n;
    }

    private boolean hasWaiterPredecessor(Node n) {
        Node p = head;
        while (p != null && !p.flag && (p = p.next) != n) ;
        return p != n;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值