Java ReentrantLock锁源码走读

目录

    • 多线程例子程序:两个线程累加共享变量,结果正确
    • 非公平锁加锁(即` lock.lock();`)过程
    • 非公平锁解锁(` lock.unlock();`)过程
    • 公平锁
      • 公平锁的加锁逻辑
      • 公平锁的释放锁逻辑

多线程例子程序:两个线程累加共享变量,结果正确

public class Test {

    static int count = 0;
    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        Runnable runnable = new Runnable() {
            @Override
            public void run () {
                try {
                    lock.lock();
                    for (int i = 0; i < 10000; i++) {
                        count++;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.out.println(count);
    }
}

非公平锁加锁(即 lock.lock();)过程

Java ReentrantLock锁源码走读_第1张图片

  1. ReentrantLock的lock方法 走到 同步器的 acquire(1)方法
  2. 走到aqs的acquire的方法,而其tryAcquire方法实现则是在NonfairSync类中
  3. NonfairSync执行tryAcquire方法又回到Sync的nonfairTryAcquire方法
  4. Sync的nonfairTryAcquire方法则如下

相关代码 java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    /**
     * Performs non-fair tryLock.  tryAcquire is implemented in
     * subclasses, but both need nonfair try for trylock method.
     */
    @ReservedStackAccess
    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;
    }
  1. 进行cas自旋获取,成功则设置当前线程获取到锁(此时state=1),否则一直自旋。
  2. 如果当前线程就是获取到锁的线程,则为可重入,此时 state > 1
  3. 其中 private volatile int state; volatile保证可见性、有序性,不保证原子性(CAS操作保证原子性)

可以看到

  1. 当一个线程lock获取到锁后成功返回,并执行自己的代码
  2. 其它线程获取不到锁,则一直自旋在哪里

非公平锁解锁( lock.unlock();)过程

释放锁则相对简单,如下图所示

Java ReentrantLock锁源码走读_第2张图片

相关代码java.util.concurrent.locks.ReentrantLock.Sync#tryRelease

@ReservedStackAccess
 protected final boolean tryRelease(int releases) {
       int c = getState() - releases;
       if (Thread.currentThread() != getExclusiveOwnerThread())
           throw new IllegalMonitorStateException();
       boolean free = false;
       if (c == 0) {
           free = true;
           setExclusiveOwnerThread(null);
       }
       setState(c);
       return free;
   }

公平锁

从上面非公平锁可以看到,加锁操作时看哪个线程cas抢到就行,至于谁抢到无所谓。可能有线程永远抢不到,即线程饥饿。这就出现了不公平的现象了

new ReentrantLock(true);就得到了一个公平锁

public ReentrantLock(boolean fair) {
      sync = fair ? new FairSync() : new NonfairSync();
}

公平锁的加锁逻辑

体现在java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
  1. 会先判断当前线程前面是否有等待的线程,即利用asq的等待队列进行判断

    • 如果有,则自己加入到链表尾部;返回false
    • 如果队列为空或者自己就是链表头;返回true
  2. hasQueuedPredecessors返回false,则可以进行cas获取锁操作了

  3. hasQueuedPredecessors返回true,那么自己则要排队了,先来后到(公平),不用cas操作了,直接加锁失败

公平锁的释放锁逻辑

同非公平锁

你可能感兴趣的:(#,Java,java,开发语言)