显式锁(第十三章)

简介:

显式锁

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

相关文章
|
4月前
|
SQL JSON 数据库
influxdb 端点使用http进行sql查询,写数据
influxdb 端点使用http进行sql查询,写数据
272 0
|
4月前
|
SQL 存储 API
ES 实战复杂sql查询、修改字段类型
ES 实战复杂sql查询、修改字段类型
104 1
|
编解码 前端开发 算法
基于OpenCV的双目摄像头测距(误差小)
首先进行双目摄像头定标,获取双目摄像头内部的参数后,进行测距;本文的双目视觉测距是基于BM算法。注意:双目定标的效果会影响测距的精准度,建议大家在做双目定标时,做好一些(尽量让误差小)。
11027 3
基于OpenCV的双目摄像头测距(误差小)
|
存储 关系型数据库 MySQL
从一个案例深入剖析InnoDB隐式锁和可见性判断(4)
从一个案例深入剖析InnoDB隐式锁和可见性判断
692 0
|
SQL 关系型数据库 MySQL
从一个案例深入剖析InnoDB隐式锁和可见性判断(2)
从一个案例深入剖析InnoDB隐式锁和可见性判断
146 0
从一个案例深入剖析InnoDB隐式锁和可见性判断(2)
|
NoSQL 关系型数据库 索引
从一个案例深入剖析InnoDB隐式锁和可见性判断(1)
从一个案例深入剖析InnoDB隐式锁和可见性判断
150 0
从一个案例深入剖析InnoDB隐式锁和可见性判断(1)
|
关系型数据库 MySQL 索引
从一个案例深入剖析InnoDB隐式锁和可见性判断(3)
从一个案例深入剖析InnoDB隐式锁和可见性判断
115 0
|
PHP 开发工具 对象存储
tp5调用阿里云oss上传文件
tp5调用阿里云oss上传文件
550 0
tp5调用阿里云oss上传文件
|
XML SQL 数据可视化
Jasperreports+jaspersoft studio快速入门
Jasperreports+jaspersoft studio快速入门
1389 0
Jasperreports+jaspersoft studio快速入门
|
机器学习/深度学习 分布式计算 算法
腾讯大数据将开源高性能计算平台 Angel,机器之心专访开发团队
随着近年来深度学习技术的发展,各种机器学习平台也纷纷涌现或从专用走向了开源。到现在,一家科技巨头没有一个主导的机器学习平台都不好意思跟人打招呼。比如谷歌有 TensorFlow、微软有 CNTK、Facebook 是 Torch 的坚定支持者、IBM 强推 Spark、百度开源了 PaddlePaddle、亚马逊也在前段时间高调宣布了对 MXNet 的支持。 现在,腾讯也加入了这一浪潮。在 12 月 18 日于深圳举办的腾讯大数据技术峰会暨 KDD China 技术峰会上,腾讯大数据宣布推出了面向机器学习的「第三代高性能计算平台」——Angel,并表示将于 2017 年一季度开放其源代码。
486 0
腾讯大数据将开源高性能计算平台 Angel,机器之心专访开发团队