ReentrantLock可重入锁的理解和源码简单分析

Posted 指尖上的艺术

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReentrantLock可重入锁的理解和源码简单分析相关的知识,希望对你有一定的参考价值。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author admin
 * @date 2018/1/16 12:16
 * ReentrantLock 可重入锁:
 * 1、启动两个线程,线程A获取锁,然后执行;同时线程B进来后,一直阻塞,直到线程A释放锁之后,线程B才接着执行
 */
public class ReentrantLockTest {
    ReentrantLock lock = new ReentrantLock();

    public void reentrantLockRun1(String threadName) {
        System.out.println(threadName + "进入");
        lock.lock();
        System.out.println(threadName + "方法被锁");
        try {
            System.out.println(threadName + "方法执行");
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println(threadName + "锁被释放");
        }
    }

    public static void main(String[] args) {
        ReentrantLockTest rltest = new ReentrantLockTest();
        Thread thread = new Thread() {
            public void run() {
                rltest.reentrantLockRun1("线程A");
            }
        };
        thread.start();

        Thread thread2 = new Thread() {
            public void run() {
                rltest.reentrantLockRun1("线程B");
            }
        };
        thread2.start();
    }
}

运行结果:
线程A进入
线程A方法被锁
线程A方法执行
线程B进入
线程A锁被释放
线程B方法被锁
线程B方法执行
线程B锁被释放

 

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author admin
 * @date 2018/1/16 12:16
 * ReentrantLock 可重入锁:
 * 1、启动两个线程,线程A获取锁,然后执行,不释放锁,接着线程A再调用reentrantLockRun2,不需要阻塞,接着执行,最后释放锁;说明同一个线程对ReentrantLock可重复获取
 * 2、线程B在这个过程中一直阻塞,等到线程A把所有的锁释放完之后,再获取锁,执行方法,最后释放锁
 */
public class ReentrantLockTest2 {
    ReentrantLock lock = new ReentrantLock();

    public ReentrantLock reentrantLockRun1(String threadName) {
        System.out.println(threadName + "进入");
        lock.lock();
        System.out.println(threadName + "方法被锁");
        try {
            System.out.println(threadName + "方法执行");
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return lock;
    }

    public ReentrantLock reentrantLockRun2(String threadName) {
        System.out.println(threadName + "进入");
        lock.lock();
        System.out.println(threadName + "方法被锁");
        try {
            System.out.println(threadName + "方法执行");
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return lock;
    }

    public static void main(String[] args) {
        ReentrantLockTest2 rltest = new ReentrantLockTest2();
        Thread thread = new Thread() {
            public void run() {
                ReentrantLock lock1 = rltest.reentrantLockRun1("线程A");
                ReentrantLock lock2  = rltest.reentrantLockRun2("线程A2");
                lock1.unlock();
                System.out.println("线程A释放锁");
                lock2.unlock();
                System.out.println("线程A2释放锁");

            }
        };
        thread.start();

        Thread thread2 = new Thread() {
            public void run() {
                ReentrantLock lock = rltest.reentrantLockRun1("线程B");
                lock.unlock();
                System.out.println("线程B释放锁");

            }
        };
        thread2.start();
    }
}

运行结果:
线程A进入
线程A方法被锁
线程A方法执行
线程B进入
线程A2进入
线程A2方法被锁
线程A2方法执行
线程A释放锁
线程A2释放锁
线程B方法被锁
线程B方法执行
线程B释放锁

 

根据源码发现:维护了这个可见性变量state ;同一个线程对可重入锁体现用state标记作累加,int nextc = c + acquires;

private volatile int state;

 

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

 

 

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

// 判断是不是第一次获取锁,如果是操作state=1;否则判断是不是同一个线程如果是state+1,如果不是同一个线程直接返回false
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;
}
}
// 大概就是用一个链表来维护等待线程
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);
}
}
 

 

以上是关于ReentrantLock可重入锁的理解和源码简单分析的主要内容,如果未能解决你的问题,请参考以下文章

ReentrantLock可重入锁原理

ReenTrantLock可重入锁(和synchronized的区别)总结

ReentrantLock可重入锁的使用场景

ReentrantLock可重入锁的原理及使用场景

java ReentrantLock可重入锁的使用场景

ReentrantLock可重入锁在我们的代码中。