ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全
使用示例
ReentrantLock lock = new ReentrantLock(); //参数默认false,不公平锁
ReentrantLock lock = new ReentrantLock(true); //公平锁
//加锁
lock.lock();
try {
//临界区
} finally {
// 解锁
lock.unlock();
}
测试
public class ReentrantLockDemo {
private static int sum = 0;
private static final 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);
}
}
//运行结果:30000
@Slf4j
public class ReentrantLockDemo2 {
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
method1();
}
public static void method1() {
lock.lock();
try {
log.debug("execute method1");
method2();
} finally {
lock.unlock();
}
}
public static void method2() {
lock.lock();
try {
log.debug("execute method2");
method3();
} finally {
lock.unlock();
}
}
public static void method3() {
lock.lock();
try {
log.debug("execute method3");
} finally {
lock.unlock();
}
}
}
运行结果
22:25:01.850 [main] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo2 - execute method1
22:25:01.863 [main] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo2 - execute method2
22:25:01.863 [main] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo2 - execute method3
@Slf4j
public class ReentrantLockDemo3 {
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
log.debug("t1启动...");
try {
lock.lockInterruptibly();
try {
log.debug("t1获得了锁");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("t1等锁的过程中被中断");
}
}, "t1");
lock.lock();
try {
log.debug("main线程获得了锁");
t1.start();
//先让线程t1执行
Thread.sleep(1000);
t1.interrupt();
log.debug("线程t1执行中断");
} finally {
lock.unlock();
}
}
}
运行结果
22:25:45.220 [main] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo3 - main线程获得了锁
22:25:45.237 [t1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo3 - t1启动…
22:25:46.247 [main] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo3 - 线程t1执行中断
22:25:46.247 [t1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo3 - t1等锁的过程中被中断
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo3.lambda$main$0(ReentrantLockDemo3.java:17)
at java.lang.Thread.run(Thread.java:748)
@Slf4j
public class ReentrantLockDemo4 {
public static void main(String[] args) throws InterruptedException {
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();
//先让线程t1执行
Thread.sleep(2000);
} finally {
lock.unlock();
}
}
}
运行结果
22:32:14.464 [main] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo4 - main线程获得了锁
22:32:14.480 [t1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo4 - t1启动…
22:32:14.480 [t1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo4 - t1获取锁失败,立即返回false
@Slf4j
public class ReentrantLockDemo4 {
public static void main(String[] args) throws InterruptedException {
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();
//先让线程t1执行
Thread.sleep(2000);
} finally {
lock.unlock();
}
}
}
运行结果
22:29:02.020 [main] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo4 - main线程获得了锁
22:29:02.033 [t1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo4 - t1启动…
22:29:03.035 [t1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo4 - 等待 1s 后获取锁失败,返回
ReentrantLock 默认是不公平的
@Slf4j
public class ReentrantLockDemo5 {
public static void main(String[] args) throws InterruptedException {
//ReentrantLock lock = new ReentrantLock(true); //公平锁
ReentrantLock lock = new ReentrantLock(); //非公平锁
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();
}
// 1s 之后去争抢锁
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();
}
}
}
运行结果
可以看出,在队列中的线程没有执行完的情况下,还是可以插队执行
但是从性能角度来说,非公平锁的性能是要高于公平锁的
java.util.concurrent类库中提供Condition类来实现线程之间的协调。调用Condition.await() 方法使线程等待,其他线程调用Condition.signal() 或Condition.signalAll() 方法唤醒等待的线程
注意:调用Condition的await()和signal()方法,都必须在lock保护之内
@Slf4j
public class ReentrantLockDemo6 {
private static final ReentrantLock lock = new ReentrantLock();
private static final Condition cigCon = lock.newCondition();
private static final Condition takeCon = lock.newCondition();
private static boolean hashCig = false;
private static boolean hasTakeout = false;
//送烟
public void cigratee() {
lock.lock();
try {
while (!hashCig) {
try {
log.debug("没有烟,歇一会");
cigCon.await();
} catch (Exception e) {
e.printStackTrace();
}
}
log.debug("有烟了,干活");
} finally {
lock.unlock();
}
}
//送外卖
public void takeout() {
lock.lock();
try {
while (!hasTakeout) {
try {
log.debug("没有饭,歇一会");
takeCon.await();
} catch (Exception e) {
e.printStackTrace();
}
}
log.debug("有饭了,干活");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockDemo6 test = new ReentrantLockDemo6();
new Thread(test::cigratee).start();
new Thread(test::takeout).start();
new Thread(() -> {
lock.lock();
try {
hashCig = true;
log.debug("唤醒送烟的等待线程");
cigCon.signal();
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
lock.lock();
try {
hasTakeout = true;
log.debug("唤醒送饭的等待线程");
takeCon.signal();
} finally {
lock.unlock();
}
}, "t2").start();
}
}
运行结果
22:40:07.812 [Thread-0] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo6 - 没有烟,歇一会
22:40:07.825 [Thread-1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo6 - 没有饭,歇一会
22:40:07.825 [t1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo6 - 唤醒送烟的等待线程
22:40:07.825 [t2] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo6 - 唤醒送饭的等待线程
22:40:07.825 [Thread-0] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo6 - 有烟了,干活
22:40:07.825 [Thread-1] DEBUG com.example.demo.seven_two_six.concurrent.aqs.reentrantlock.ReentrantLockDemo6 - 有饭了,干活
借鉴fox大佬的源码分析图:https://www.processon.com/view/link/6191f070079129330ada1209
源码逻辑图:
公平锁、非公平锁