• 2、AQS之ReentrantLock详解


    ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全

    ReentrantLock特点

    • 可中断
    • 可以设置超时时间
    • 可以设置为公平锁
    • 支持多个条件变量
    • 与 synchronized 一样,都支持可重入

    synchronized和ReentrantLock的区别

    • synchronized是JVM层次的锁实现,ReentrantLock是JDK层次的锁实现;
    • synchronized的锁状态是无法在代码中直接判断的,但是ReentrantLock可以通过ReentrantLock#isLocked判断;
    • synchronized是非公平锁,ReentrantLock是可以是公平也可以是非公平的;
    • synchronized是不可以被中断的,而ReentrantLock#lockInterruptibly方法是可以被中断的;
    • 在发生异常时synchronized会自动释放锁,而ReentrantLock需要开发者在finally块中显示释放锁;
    • ReentrantLock获取锁的形式有多种:如立即返回是否成功的tryLock(),以及等待指定时长的获取,更加灵活;
    • synchronized在特定的情况下对于已经在等待的线程是后来的线程先获得锁,而ReentrantLock对于已经在等待的线程是先来的线程先获得锁

    应用

    使用示例

    ReentrantLock lock = new ReentrantLock(); //参数默认false,不公平锁
    ReentrantLock lock = new ReentrantLock(true); //公平锁
    //加锁
    lock.lock();
    try {
    	//临界区
    } finally {
    	// 解锁
    	lock.unlock();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    测试

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    可重入

    @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();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    运行结果

    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();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行结果

    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();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    运行结果

    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();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    运行结果

    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();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果
    可以看出,在队列中的线程没有执行完的情况下,还是可以插队执行

    在这里插入图片描述


    但是从性能角度来说,非公平锁的性能是要高于公平锁的

    条件变量

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    运行结果

    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
    源码逻辑图:
    公平锁非公平锁

  • 相关阅读:
    java毕业设计仓库管理系统Mybatis+系统+数据库+调试部署
    微信小程序wxs标签 在wxml文件中编写JavaScript逻辑
    canvas 学习
    Cmake 生成器表达式
    接口测试在python、jmeter、postman工具下如何做断言?
    2022年招投标,最加分的资质证书排行榜!
    HTTP状态码301和302的区别
    golang常用库之olivere elastic包 | go操作elasticsearch
    这几个合成配音软件新手也在用
    MySQL和SQLserver中group by的区别
  • 原文地址:https://blog.csdn.net/weixin_41381248/article/details/127718970