AQS 框架之 Lock 接口
程序员文章站
2022-05-25 14:45:10
■ 前言 - 内存锁的不足 不可中断:使用内部锁(指的是 synchronized) 时,不能中断正在等待获取锁的线程 不可超时:使用内部锁时,在请求锁失败情况下,必须无限等待,没有超时效果 自动释放:使用内部锁时,内部锁必须在获取它们的代码块中被自动释放(虽然对代码来说是种简化且对异常友好) 不可 ......
■ 前言 - 内存锁的不足
- 不可中断:使用内部锁(指的是 synchronized) 时,不能中断正在等待获取锁的线程
- 不可超时:使用内部锁时,在请求锁失败情况下,必须无限等待,没有超时效果
- 自动释放:使用内部锁时,内部锁必须在获取它们的代码块中被自动释放(虽然对代码来说是种简化且对异常友好)
- 不可伸缩:使用内部锁时,无法细粒度控制锁(伸缩性不足),即无法实现锁分离和锁联结,比如为每个链表节点(或部分)加锁从而允许不同的线程能够独立操作链表的不同节点(部分),遍历或修改链表时,需先获取该节点锁并直到获取下一个节点锁时才释放当前节点锁
- 性能问题:使用内部锁时,在有竞争情况下仍会出现性能问题,尽管jdk6对内部锁进行了优化,但无论是偏向锁或是轻量级锁都是针对无竞争情况的优化,无竞争情况下与 reentractlock 性能一致,但有竞争时lock明显更高效
■ lock 接口综述
- 定义: jdk1.5 引入lock接口,其定义了一些抽象的锁操作,相比synchronized,lock 提供了无条件、可轮询、可定时、可中断的锁获取操作,所有加锁和解锁的方法都是显式的
- 实现: lock 的实现必须提供具有与 synchronized 相同的内存语义,但加锁的语义、调度算法、顺序保证、性能特性可以有所不同
- 使用: lock接口的实现基本是通过聚合一个同步器 abstractqueuedsynchronized 的子类来完成线程的访问控制
- 对比内部锁: lock缺少隐式获取/释放锁的便捷,但却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种内部锁不具备的同步性,甚至还支持读写锁分离,同时允许获取和释放可以不在同一个块中
- 补充:此番为 aqs 框架之综述 (排期中) 的子番,同时也是 aqs 框架之 reentractlock (排期中) 的预备番
并发包中的lock接口实现类,其中readlock和writelock是reentrantreadwritelock的静态内部类,如图:
■ lock 接口方法标准使用
//标准用法 lock lock = new reentrantlock(); //获取锁应在try之前,因为若获取锁时发生异常,异常抛出同时会导致锁无故释放 lock.lock(); try{ dosometing(); }finally{ //注意:必须在finally块中释放锁,目的是保证在获取到锁之后,最终能被释放 lock.unlock(); }
■ lock 接口方重点方法
1. lock()
- lock方法应具有与内部锁加锁相同的内存语义,即无锁阻塞和支持可重入
- lock方法必须搭配unlock方法使用,同时必须在finally中显式调用unlock方法释放锁
/** * acquires the lock. * 获取锁,调用该方法的当前线程将会获取锁,当锁获得后,从该方法返回 * <p>if the lock is not available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until the * lock has been acquired. * 若当前锁不可用(已被占有),当前线程会一直休眠直到锁为可被获取状态 * <p><b>implementation considerations</b> * 实现该方法的注意事项 * <p>a {@code lock} implementation may be able to detect erroneous use * of the lock, such as an invocation that would cause deadlock, and * may throw an (unchecked) exception in such circumstances. the * circumstances and the exception type must be documented by that * {@code lock} implementation. * 该方法的实现需要能发现lock被错误使用,如死锁或抛出不可查异常(即可运行期异常和error) * 此时该实现必须用文档注明其可能出现的异常或需要的使用环境 */ void lock();
2. lockinterruptibly()
- lockinterruptibly 方法提供可中断的锁获取操作并允许在可取消的活动中使用
//样例代码
public boolean dotask throws interruptedexception(){ lock.lockinterruptibly(); try{ return canceltask(); }finally{ lock.unlock(); } } //取消任务 private boolean canceltask() throws interruptedexception {...}
/** * acquires the lock unless the current thread is * {@linkplain thread#interrupt interrupted}. * 可中断地获取锁,即在锁的获取中可以中断当前线程 * <p>acquires the lock if it is available and returns immediately. * 当获取锁时锁可用就立即返回 * <p>if the lock is not available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: * <ul> * <li>the lock is acquired by the current thread; or * <li>some other thread {@linkplain thread#interrupt interrupts} the * current thread, and interruption of lock acquisition is supported. * </ul> * 若当前锁不可用(已被占有),当前线程会一直休眠直到以下两种情况发生: * 1.锁被当前线程获取 * 2.其他线程中断当前线程,同时锁的获取允许被中断 * <p><b>implementation considerations</b> * 实现该方法的注意事项 * <p>the ability to interrupt a lock acquisition in some * implementations may not be possible, and if possible may be an * expensive operation. the programmer should be aware that this * may be the case. an implementation should document when this is * the case. * 该方法属于拓展方法,只有需要中断服务的时候才需要实现它 * <p>an implementation can favor responding to an interrupt over * normal method return. * 相对于返回,该方法更适合抛出一个中断响应,比如中断异常 * @throws interruptedexception if the current thread is * interrupted while acquiring the lock (and interruption * of lock acquisition is supported) */ void lockinterruptibly() throws interruptedexception;
3. trylock()
- trylock 方法提供可定时与可轮询的锁获取方式,与无条件的锁获取相比,具有更完善的错误恢复机制
- trylock 方法能够有效的防止死锁的发生,比如使用轮询锁优雅失败规避死锁
- trylock 方法同时提供定时锁的功能,其允许在限时活动内部使用独占锁,当线程获取锁、被中断或超时后返回
- trylock 方法支持轮询获取锁:通过一个循环配合trylock()来不断尝试获取锁,由于trylock()非阻塞因此会立即返回是否成功获取锁的结果;当不能获取所有的锁时,应释放已获得的所有锁并重新尝试获取
- trylock 方法同时支持响应中断
/** * acquires the lock only if it is free at the time of invocation. * 尝试非阻塞的获取锁,调用该方法后立即返回是否成功获取锁true/false * <p>acquires the lock if it is available and returns immediately * with the value {@code true}. * if the lock is not available then this method will return * immediately with the value {@code false}. * 当锁不可用时立即返回false * this usage ensures that the lock is unlocked if it was acquired, and * doesn't try to unlock if the lock was not acquired. * 该实现应确保当锁被获取时是未锁状态,当未被获取时不会尝试解锁 * @return {@code true} if the lock was acquired and * {@code false} otherwise */ boolean trylock(); /** * acquires the lock if it is free within the given waiting time and the * current thread has not been {@linkplain thread#interrupt interrupted}. * 没有被中断当前线程在指定超时时间内获取锁 * if the lock is not available then the current thread becomes disabled for * thread scheduling purposes and lies dormant until one of three things happens: * <ul> * <li>the lock is acquired by the current thread; or * <li>some other thread {@linkplain thread#interrupt interrupts} the * current thread, and interruption of lock acquisition is supported; or * <li>the specified waiting time elapses * </ul> * <p>if the specified waiting time elapses then the value {@code false} is returned. * if the time is less than or equal to zero, the method will not wait at all. * 当前线程在以下三种情况下会返回: * 1.当前线程在超时时间内获得锁 * 2.当前线程在超时时间内被中断 * 3.超时时间结束,返回false,线程不再被阻塞 * @param time the maximum time to wait for the lock * @param unit the time unit of the {@code time} argument * @return {@code true} if the lock was acquired and {@code false} * if the waiting time elapsed before the lock was acquired * @throws interruptedexception if the current thread is interrupted * while acquiring the lock (and interruption of lock * acquisition is supported) */ boolean trylock(long time, timeunit unit) throws interruptedexception;
4. unlock()
- 使用lock方法、lockinterruptibly方法、trylock方法都必须显式调用unlock方法释放锁
- unlock方法必须在finally块中执行,这也是lock使用的一个代码隐患(容易忘记执行)
- unlock方法允许与lock方法不在同一个块(即{})中执行,但业务代码必须保证在try-finally块中执行
/** * releases the lock. * 释放锁 */ void unlock();
5. newcondition 方法
- lock搭配condition可以实现更加灵活的锁获取与释放的条件控制
/** * returns a new {@link condition} instance that is bound to this * {@code lock} instance. * 返回一个等待通知组(条)件 * <p>before waiting on the condition the lock must be held by the current thread. * a call to {@link condition#await()} will atomically release the lock * before waiting and re-acquire the lock before the wait returns. * 该组件与当前锁绑定,当先线程只有获得锁才能调用该组件的await方法并释放锁 * @return a new {@link condition} instance for this {@code lock} instance * @throws unsupportedoperationexception if this {@code lock} * implementation does not support conditions */ condition newcondition();
下一篇: 华为机试 字符串最后一个单词的长度
推荐阅读
-
mvc框架打造笔记之wsgi协议的优缺点以及接口实现
-
AQS 框架之 Lock 接口
-
Java基础之Collections框架Map接口实现类HashMap及其源码分析(1)
-
AQS 框架之 Unsafe 源码详解
-
现有web系统替换成Spring Boot2框架 之21 同时支持带项目名和不带项目名访问,web访问强制https,接口保持http访问 Spring Boot2带项目名不带项目名httpshttp
-
mvc框架打造笔记之wsgi协议的优缺点以及接口实现
-
AQS 框架之 Lock 接口
-
java集合框架概述之Map接口
-
Java基础之Collections框架Map接口实现类HashMap及其源码分析(1)
-
AQS 框架之 Unsafe 源码详解