ReentrantLock分析

ReentrantLock用于java的多线程编程中对临界资源的互斥访问,作为synchronized的补充。

ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            try {
                lock.lock();
                System.out.println(100);
            } finally {
                lock.unlock();
            }
        });
        Thread t2 = new Thread(() -> {
            //if (lock.tryLock())
            try {
                lock.lock();
                System.out.println(200);
            } finally {
                lock.unlock();
            }
        });
        Thread t3 = new Thread(() -> {
            try {
                lock.lock();
                System.out.println(300);
            } finally {
                lock.unlock();
            }
        });
        t1.start();
        t2.start();
        t3.start();

借助于上述代码分析ReentrantLock的实现:
假设线程t1获取锁,此时调用

public void lock() {
        sync.lock();
    }

sync为ReentrantLock的实例成员,根据构造函数的参数设置为公平锁和非公平锁(默认)。此时sync为非公平锁。调用NonfairSync类的

final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

这里先判断state变量是不是0,是0则设置为1,并且把当前持有排他锁的线程设置为当前线程(这里为t1),此时表明t1获取到锁。这个时候如果线程t2再获取锁的话则进入else分支。

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

在else分支里,首先调用tryAcquire,最终调到

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

先判断state是否为0,此时因为t1获取锁肯定不为0,再看当前获取锁的线程是不是和持有排他锁的线程是同一个线程,这里表明ReentrantLock是递归锁,同一个线程中多次执行获取锁的代码时线程不会被阻塞。如果都不是则返回false表明获取锁失败。这个时候就要把当前线程加入到等待队列中。

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

这里mode为null,在非公平锁中没有用到。先看队列的尾部是不是null,如果tail不为null,表明已经有线程加入到了等待队列,这个时候只要把node节点加入到队列中就可以。调用compareAndSetTail是防止别的线程此时也要加入到等待队列,这里对等待队列的修改需要互斥。如果tail不为null则调用

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

第一遍循环里面,t为null,将队列的head设置为头结点(空的Node),并把tail指向该节点。第二遍循环就是双链表的常规操作,加入node节点,并设置tail,此时返回的是那个空节点,而不是当前的node。而在addWaiter里返回的是新加入的节点。

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

这里先判断node节点的前驱是不是head,如果是head并且获取锁成功则要重新设置head。否则把当前节点的前驱的waitStatue设置为SIGNAL(-1),表明他的下一个节点需要在当前锁被释放时被唤醒。最后调用parkAndCheckInterrupt()把当前线程挂起。t3获取锁的时候同样失败被加入到等待队列。
在这里插入图片描述
当调用lock.unlock()唤醒其他线程时可以看到,从等待队列里面摘下一个节点并唤醒其投入运行要经过不少的步骤,此时如果有其他线程试图获取锁(调用lock函数)则大概率通过了if条件中的compareAndSetState测试,此时该线程获取到了锁,被摘下的线程获取锁失败继续被挂起,这就是非公平锁,但正是由于这样增加了效率,导致非公平锁的效率相对于公平锁更高(公平锁则要按照等待队列中的顺序依次唤醒线程获取锁)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值