博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
显式锁(第十三章)
阅读量:5751 次
发布时间:2019-06-18

本文共 2458 字,大约阅读时间需要 8 分钟。

hot3.png

显式锁

在Java5.0之前,在协调对共享对象的访问时可以使用的机制只有synchronized和volatile。Java5.0增加了一种新的机制:ReentrantLock。ReentrantLock并不是一种替代内置加锁的方法,而是当内置加锁机制不适用时,作为一种可选择的高级功能。

1. Lock接口与ReentrantLock

Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显示的。

Lock接口:

public interface Lock {	void lock();    void lockInterruptibly throws InterruptedException;    boolean tryLock();    boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;    void unlock();    Condition newCondition();}

ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。

Lock接口的使用形式:

Lock lock = new ReentrantLock();...lock.lock();try {	//更新对象状态    //捕获异常,并在必要时恢复不变性条件} finally {	lock.unlock();}
使用Lock时,必须在finally块中释放锁,否则,如果被保护的代码中抛出了异常,那么这个锁永远都无法释放。这就是ReentrantLock不能完全替代synchronized的原因:它更加危险,因为程序的执行控制离开被保护的代码块时,不会自动清除锁。

2. 轮询锁与定时锁

可定时的与可轮训的锁获取模式是由tryLock方式实现的,与无条件的锁获取模式相比,它具有更完善的错误恢复机制。

可定时的与可轮询的锁能够避免死锁的发生。如果不能获得所有需要的锁,那么可以使用可定时的或可轮询的锁获取方式,从而使你重新获得控制权,它会释放已经获得的锁,然后重新尝试获取所有锁。

在实现具有时间限制的操作时,定时锁能够提供一个时限,如果操作不能在指定的时间内给出结果,那么就会使程序提前结束。

3. 可中断的锁获取操作

Java中,请求内置锁时不能响应中断。Lock的lockInterruptibly方法能够在锁的同时保持对中断的响应,且由于它包含在Lock中,因此无须创建其他类型的不可中断阻塞机制。

...lock.lockInterruptibly();...

定时的tryLock同样能响应中断,因此当需要实现一个定时的和可中断的锁获取操作时,可是使用tryLock方法。

4. 公平性

在ReentrantLock的构造函数中提供了两种公平性选择:非公平(默认)、公平。 在公平的锁上,线程按照它们发出请求的顺序来获得锁,但在非公平的锁上,则允许“插队”:当一个线程请求非公平的锁时,如果在发出请求的同时该锁的状态变为可用,那么这个线程将跳过队列中所有的等待线程并获得这个锁。

在大多数情况下,非公平锁的性能要高于公平锁的性能。

当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。在这些情况下,“插队”带来的吞吐量提升(当锁处于可用状态时,线程却还处于被唤醒的过程中)则可能不会出现。

与默认的ReentrantLock一样,内置锁并不会提供确定的公平性保证,但在大多数情况下,在锁实现上实现统计上的公平性保证已经足够了。Java语言规范并没有要求JVM以公平的方式来实现内置锁,而在各种JVM中也没有这样做。ReentrantLock并没有进一步降低锁的公平性,而只是使一些已经存在的内容更明显。

5.在synchronized和ReentrantLock之间进行选择

  • ReentrantLock的优点: ReentrantLock在加锁和内存上提供的语义与内置锁相同,此外它还提供了一些其他功能,包括定时的锁等待、可中断的锁等待、公平性,以及实现非块结构的加锁

  • ReentrantLock的缺点: ReentrantLock的危险性比同步机制要高,如果忘记在finally块中调用unlock,那么就有可能出现问题。

    两者之间的选择:  当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮训的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应该优先使用synchronized。

6. 读-写锁

一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。 要读取由ReadWriteLock保护的数据,必须首先获得读取锁,当需要修改ReadWriteLock保护的数据时,必须首先获得写入锁。尽管这两个锁看上去是彼此独立的,但读取锁和写入锁只是读-写锁对象的不同视图。

public interface ReadWriteLock {	Lock readLock();    Lock writeLock();}

ReentranReadWriteLock为这两种锁都提供了可重入的加锁语义。与ReentrantLock类似,ReentrantReadWriteLock在构造时也可以选择是一个非公平的锁(默认)还是一个公平的锁。 在公平的锁中,等待事件最长的线程将优先获得锁。如果这个锁由读线程持有,而另一个线程请求写入锁,那么其他读线程都不能获得读取锁,直到写线程使用完并且释放了写入锁。 在非公平的锁中,线程获得访问许可的顺序是不确定的。写线程降级为读线程是可以的,但从读线程升级为写线程则是不可以的(这样做会导致死锁)。

转载于:https://my.oschina.net/u/2602561/blog/1560132

你可能感兴趣的文章
OracleLinux安装说明
查看>>
nova分析(7)—— nova-scheduler
查看>>
Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)
查看>>
OpenMediaVault 搭建git,ssh无法连接问题
查看>>
java多线程之:Java中的ReentrantLock和synchronized两种锁定机制的对比 (转载)
查看>>
【Web动画】SVG 实现复杂线条动画
查看>>
使用Wireshark捕捉USB通信数据
查看>>
Apache Storm 官方文档 —— FAQ
查看>>
iOS 高性能异构滚动视图构建方案 —— LazyScrollView
查看>>
Java 重载、重写、构造函数详解
查看>>
【Best Practice】基于阿里云数加·StreamCompute快速构建网站日志实时分析大屏
查看>>
【云栖大会】探索商业升级之路
查看>>
HybridDB实例新购指南
查看>>
C语言及程序设计提高例程-35 使用指针操作二维数组
查看>>
华大基因BGI Online的云计算实践
查看>>
排序高级之交换排序_冒泡排序
查看>>
Cocos2d-x3.2 Ease加速度
查看>>
[EntLib]关于SR.Strings的使用办法[加了下载地址]
查看>>
中小型网站架构分析及优化
查看>>
写shell的事情
查看>>