ReentrantLock是什么?
- ReentrantLock是一种基于AQS框架的应用实现。
- 是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。
ReentrantLock与synchronized的区别
- synchronized是JVM层次的锁实现,ReentrantLock是JDK层次的锁实现;
- synchronized的锁状态是无法在代码中直接判断的,但是ReentrantLock可以通过ReentrantLock#isLocked判断;
- synchronized是非公平锁,ReentrantLock是可以是公平也可以是非公平的;
- synchronized是不可以被中断的,而ReentrantLock#lockInterruptibly方法是可以被中断的;
- 在发生异常时synchronized会自动释放锁,而ReentrantLock需要开发者在finally块中显示释放锁;
- ReentrantLock获取锁的形式有多种:如立即返回是否成功的tryLock(),以及等待指定时长的获取,更加灵活;
- synchronized在特定的情况下对于已经在等待的线程是后来的线程先获得锁,而ReentrantLock对于已经在等待的线程是先来的线程先获得锁;
ReentrantLock的使用方式
ReentrantLock lock = new ReentrantLock();
ReentrantLock lock = new ReentrantLock(true);
lock.lock();
try {
} finally {
lock.unlock();
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Tset1 {
private static int sum = 0;
private static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(() -> {
lock.lock();
try {
for (int j = 0; j < 10000; j++) {
sum++;
}
} finally {
lock.unlock();
}
});
thread.start();
}
Thread.sleep(2000);
System.out.println(sum);
}
}
ReentrantLock的可重入机制
import java.util.concurrent.locks.ReentrantLock;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Tset2 {
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
method1();
}
public static void method1() {
lock.lock();
try {
log.debug("执行 method1");
method2();
} finally {
lock.unlock();
}
}
public static void method2() {
lock.lock();
try {
log.debug("执行 method2");
method3();
} finally {
lock.unlock();
}
}
public static void method3() {
lock.lock();
try {
log.debug("执行 method3");
} finally {
lock.unlock();
}
}
}

ReentrantLock的可中断机制(立即中断)
- 应用场景:一个类只需要一个线程去初始化,其他线程初始化完成后当前线程不需要继续处理。
import java.util.concurrent.locks.ReentrantLock;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Tset3 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1启动...");
if (!lock.tryLock()) {
log.debug("t1获取锁失败,立即返回false");
return;
}
try {
log.debug("t1获得了锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
try {
log.debug("main线程获得了锁");
t1.start();
try {
while (true) {
}
} catch (Exception e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}

ReentrantLock的锁超时超时失败机制
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Tset4 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1启动...");
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
log.debug("等待 1s 后获取锁失败,返回");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
try {
log.debug("t1获得了锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
try {
log.debug("main线程获得了锁");
t1.start();
try {
while (true) {
}
} catch (Exception e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}

ReentrantLock的公平锁方式机制
- new ReentrantLock(true);的时候穿一个true就是公平锁,不传或者传一个false就是非公平锁!
import java.util.concurrent.locks.ReentrantLock;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Tset5 {
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock(true);
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "t" + i).start();
}
Thread.sleep(1000);
for (int i = 0; i < 500; i++) {
new Thread(() -> {
lock.lock();
try {
log.debug(Thread.currentThread().getName() + " running...");
} finally {
lock.unlock();
}
}, "强行插入" + i).start();
}
}
}
- 公平锁运行结果:按照进入的顺序执行,正常顺序日志,无突出的重点,不截图了。
- 非公平锁运行(定义ReentrantLock的时候不传值)结果:出现插队的线程!

ReentrantLock的阻塞唤醒机制
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Tset6 {
private static ReentrantLock lock = new ReentrantLock();
private static Condition cigCon = lock.newCondition();
private static boolean hashcig = false;
public static void main(String[] args) {
new Thread(() -> {
lock.lock();
log.debug("需要被唤醒的线程加锁");
try {
while (!hashcig) {
try {
log.debug("需要被唤醒的线程等待!");
cigCon.await();
} catch (Exception e) {
e.printStackTrace();
}
}
log.debug("需要被唤醒的线程跳出循环!");
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
lock.lock();
try {
hashcig = true;
cigCon.signal();
} finally {
lock.unlock();
}
}, "t1").start();
}
}

结束语
- 获取更多有价值的文章,让我们一起成为架构师!
- 关注公众号,可以让你对MySQL有非常深入的了解
- 关注公众号,每天持续高效的了解并发编程!
- 关注公众号,后续持续高效的了解spring源码!
- 这个公众号,无广告!!!每日更新!!!
