您的位置:首页 > 编程语言 > Java开发

Java并发读书学习笔记(十)——显式锁

2018-04-02 15:00 429 查看

10.1 Lock和ReentrantLock

ReentrantLock实现了Lock接口,并提供了synchronized相同的互斥性与内存可见性。在获取ReentrantLock时,有着进入同步代码块的内存语义,在释放ReentrantLock时,同样有着与退出同步代码块相同的内存语义。此外,与synchronized一样,ReentrantLock还提供可重入的加锁语义。ReentrantLock支持在Lock接口中定义的所有获取锁模式,并且与synchronized相比,它还为处理锁的不可用性问题提供了更高的灵活性。
10.1.1 轮询锁和定时锁可定时和可轮询的锁的获取模式是由tryLock方法实现的,与无条件的锁获取模式相比,它具有更完善的错误恢复机制。在内置锁中,死锁是一个严重的问题,恢复程序的唯一方法时重写启动程序,而防止死锁的唯一方法就是在构造程序时避免出现不一致的锁顺序。而可定时和可轮询的锁提供了另一种选择。
10.1.2 可中断的锁获取操作正如定时的锁获取的操作能在带有时间限制地操作中使用独占锁,可中断的锁获取操作能在可取消的操作中使用加锁。这些不可中断的阻塞机制将使得实现可取消的任务变得复杂。lockInterruptibly方法能够在获得锁的同时保持对中断的响应,并且由于它包含在Lock中,因此无须创建其他类型的不可中断阻塞机制。
10.1.3 非块结构的加锁在内置锁中,锁的获取和释放等操作都是基于代码块的。释放锁的操作总是与获取锁的操作处于同一代码块,而不考虑控制权如何退出该代码块。自动的锁释放操作简化了对程序的分析,避免了可能的编码错误,但有时候需要更灵活的加锁规则。

10.2 公平性

在ReentrantLock中提供了两种公平性选择:创建一个非公平的锁或者公平的锁。在公平的锁上,线程将按照它们发出请求的顺序来获得锁,但在非公平的锁上,则允许插队:当一个线程请求非公平的锁时,如果发出请求的同时该锁的状态变为可用,那么线程将跳过队列中所有的等待线程并获得这个锁。非公平的ReentrantLock并不提倡插队行为,但无法防止在某个线程在合适的时候进行插队。在公平的锁中,如果有一个线程持有这个锁或者有其他线程在队列中等待这个锁,新发出请求的线程将会被放入队列中。在非公平的锁中,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中。

10.3 synchronized与ReentrantLock

在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才需要使用,这些功能包括:可定时、可轮询和可中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应当优先使用synchronized。

10.4 读写锁

ReentrantLock实现了一种标准的互斥锁:每次最多只有一个线程能持有ReentrantLock。但对于维护数据的完整性来说,互斥通常是一种过于强硬的加锁规则,不必要地限制了并发性。互斥是一种保守的加锁策略,虽然可以避免写写冲突和写读冲突,但同样避免了读读冲突。数据结构上的操作大多是读,虽然它们也是可变的并且在某些情况下可修改。此时,如果能够放宽加锁需求,运行多个执行读操作的线程访问数据结构,那么将提升性能。只要每个线程都能保证读到最新的数据,并且在读取数据时不会有其他线程修改数据,那么就不会发生问题。在这种情况下就能使用读写锁:一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息