概念

相比较于synchronized它具备如下特点:

  • 可中断
  • 可以设置超时时间;
  • 可以设置公平锁(先到先得,而不是随机获取锁);
  • 支持多个条件变量;

与synchronized一样,都支持可重入;
基本语法:

  1. reentrantLock.lock();
  2. try{
  3. //临界区
  4. }finally{
  5. //释放锁
  6. reentrantLock.unlock();
  7. }

可重入

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把拥有者,因此有权利再次获取这把
如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住

  1. static ReentrantLock lock = new ReentrantLock();
  2. public static void main(String[] args) {
  3. method1();
  4. }
  5. public static void method1() {
  6. lock.lock();
  7. try {
  8. System.out.println("execute method1");
  9. method2();
  10. } finally {
  11. lock.unlock();
  12. }
  13. }
  14. public static void method2() {
  15. lock.lock();
  16. try {
  17. System.out.println("execute method2");
  18. method3();
  19. } finally {
  20. lock.unlock();
  21. }
  22. }
  23. public static void method3() {
  24. lock.lock();
  25. try {
  26. System.out.println("execute method3");
  27. } finally {
  28. lock.unlock();
  29. }
  30. }

输出

execute method1 execute method2 execute method3

可打断

  1. private static void test1() {
  2. ReentrantLock lock = new ReentrantLock();
  3. Thread t1 = new Thread(() -> {
  4. log.debug("启动...");
  5. try {
  6. lock.lockInterruptibly();
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. log.debug("等锁的过程中被打断");
  10. return;
  11. }
  12. try {
  13. log.debug("获得了锁");
  14. } finally {
  15. lock.unlock();
  16. }
  17. }, "t1");
  18. lock.lock();
  19. log.debug("获得了锁");
  20. t1.start();
  21. try {
  22. sleep(1);
  23. t1.interrupt();
  24. log.debug("执行打断");
  25. } finally {
  26. lock.unlock();
  27. }
  28. }
  1. 23:40:12.610 c.TestInterrupt [main] - 获得了锁
  2. 23:40:12.612 c.TestInterrupt [t1] - 启动...
  3. 23:40:13.615 c.TestInterrupt [main] - 执行打断
  4. 23:40:13.616 c.TestInterrupt [t1] - 等锁的过程中被打断
  5. java.lang.InterruptedException
  6. at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
  7. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
  8. at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
  9. at cn.itcast.n4.reentrant.TestInterrupt.lambda$test1$1(TestInterrupt.java:49)
  10. at java.lang.Thread.run(Thread.java:748)

注意如果是不可中断模式,那么即使使用了 interrupt 也不会让等待中断

  1. private static void test2() {
  2. ReentrantLock lock = new ReentrantLock();
  3. Thread t1 = new Thread(() -> {
  4. log.debug("启动...");
  5. lock.lock();
  6. try {
  7. log.debug("获得了锁");
  8. } finally {
  9. lock.unlock();
  10. }
  11. }, "t1");
  12. lock.lock();
  13. log.debug("获得了锁");
  14. t1.start();
  15. try {
  16. sleep(1);
  17. t1.interrupt();
  18. log.debug("执行打断");
  19. sleep(1);
  20. } finally {
  21. log.debug("释放了锁");
  22. lock.unlock();
  23. }
  24. }
  1. 18:06:56.261 [main] c.TestInterrupt - 获得了锁
  2. 18:06:56.265 [t1] c.TestInterrupt - 启动...
  3. 18:06:57.266 [main] c.TestInterrupt - 执行打断 // 这时 t1 并没有被真正打断, 而是仍继续等待锁
  4. 18:06:58.267 [main] c.TestInterrupt - 释放了锁
  5. 18:06:58.267 [t1] c.TestInterrupt - 获得了锁

锁超时

  1. private static void test2() {
  2. ReentrantLock lock = new ReentrantLock();
  3. Thread t1 = new Thread(() -> {
  4. log.debug("启动...");
  5. if (!lock.tryLock()) {
  6. log.debug("获取立刻失败,返回");
  7. return;
  8. }
  9. try {
  10. log.debug("获得了锁");
  11. } finally {
  12. lock.unlock();
  13. }
  14. }, "t1");
  15. lock.lock();
  16. log.debug("获得了锁");
  17. t1.start();
  18. try {
  19. sleep(2);
  20. } finally {
  21. lock.unlock();
  22. }
  23. }
  1. 18:15:02.918 [main] c.TestTimeout - 获得了锁
  2. 18:15:02.921 [t1] c.TestTimeout - 启动...
  3. 18:15:02.921 [t1] c.TestTimeout - 获取立刻失败,返回
  1. private static void test1() {
  2. ReentrantLock lock = new ReentrantLock();
  3. Thread t1 = new Thread(() -> {
  4. log.debug("启动...");
  5. try {
  6. if (!lock.tryLock(1, TimeUnit.SECONDS)) {
  7. log.debug("获取等待 1s 后失败,返回");
  8. return;
  9. }
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. try {
  14. log.debug("获得了锁");
  15. } finally {
  16. lock.unlock();
  17. }
  18. }, "t1");
  19. lock.lock();
  20. log.debug("获得了锁");
  21. t1.start();
  22. try {
  23. sleep(2);
  24. } finally {
  25. lock.unlock();
  26. }
  27. }
  1. 18:19:40.537 [main] c.TestTimeout - 获得了锁
  2. 18:19:40.544 [t1] c.TestTimeout - 启动...
  3. 18:19:41.547 [t1] c.TestTimeout - 获取等待 1s 后失败,返回

公平锁

  1. public class ReentrantLockFairTest {
  2. static Lock lock = new ReentrantLock(true);
  3. public static void main(String[] args) throws InterruptedException {
  4. for (int i = 0; i < 5; i++) {
  5. new Thread(new ThreadDemo(i)).start();
  6. }
  7. }
  8. static class ThreadDemo implements Runnable {
  9. Integer id;
  10. public ThreadDemo(Integer id) {
  11. this.id = id;
  12. }
  13. @Override
  14. public void run() {
  15. try {
  16. TimeUnit.MILLISECONDS.sleep(10);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. for (int i = 0; i < 2; i++) {
  21. lock.lock();
  22. try {
  23. System.out.println("获得锁的线程:" + id);
  24. } finally {
  25. lock.unlock();
  26. }
  27. }
  28. }
  29. }
  30. }

公平锁结果:
image.png
非公平锁结果:
image.png