(一)什么是显示锁


Lock子类就是显示锁.


首先synchronized是内置锁,

有了synchronized为什么还要Lock?
Java程序是靠synchronized关键字实现锁功能的,使用synchronized关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。显示锁是我们手动的去声明一个锁,手动的去拿取锁,手动的去释放锁.

synchronized拿锁取锁过程是非常固定化的,没得调用选择,synchronized没有超时机制中断机制等等.必须要拿到锁线程才能往下走.
但是对于显示锁来说,提供了机制就很多,Lock接口是显示锁的共同接口, lock()方法获取锁,然后通过unlock()来释放锁,还提供了可以拿锁的过程中(等待期间)可以中断.

tryLock()尝试拿锁的过程中,如果拿不到tryLock()就会返回false,然后程序员就可以做点其它逻辑,然后再尝试着去拿到锁,这样就可以避免线程长时间阻塞在那里.

tryLock()还可以设置等待时间,如果到了一定时间还拿不到锁,再返回false.


显示锁特性

特性 描述
尝试非阻塞地获取锁 当前线程尝试获取锁.如果这一时刻锁没有被其它线程获取到,则成功获取并持有锁.
能被中断地获取锁 与synchronized不同,获取到的锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会释放
超时获取锁 在指定的截止时间之前获取锁,如果截止时间到了仍旧无法获取锁,则返回.



1.显示锁和synchronized关键字如何使用?


如果没有用到lock接口的尝试获取锁的API,没有用到拿锁的过程中中断的特性,那么你尽量就用synchronized关键字好一点,
因为synchronized关键字被jdk优化的力度非常大,比如jdk1.8的ConcurrentHashMap用的是synchronized关键字,而在1.8之前用的还是lock接口

synchronized的资源消耗比显示锁的消耗少一点,因为synchronized是语言的特性,而Lock接口是一个类,如果用Lock需要实例化对象,就需要消耗内存,所以synchronized资源消耗要少一点.


lock接口的实现类用的比较多的就是可重入锁以及读写锁.


2.Lock的标准用法

一定要这么用,不然会出现问题.

| public void test() {
lock.lock(); //加锁
try {
age++; //业务逻辑
/ 但是一定要用finally代码块儿,万一业务逻辑被中断了(抛出了一个异常),
lock.unlock();很可能就不能执行,就无法释放锁
/
} finally {
lock.unlock(); //解锁
}
} | | —- |


在finally块中释放锁,目的是保证在获取到锁之后,最终能够被释放。(不管try里面的业务逻辑是否执行成功,在finally的锁一定会被释放)
不要将获取锁的过程写在try块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放。

3.Lock的常用API

public void lock()
获取锁。
如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。
如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回。
如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一
直处于休眠状态,此时锁保持计数被设置为 1。

指定者:
接口 Lock 中的 lock


lockInterruptibly
public void lockInterruptibly() throws InterruptedException

1)如果当前线程未被中断,则获取锁。

2)如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。

3)如果当前线程已经保持此锁,则将保持计数加 1,并且该方法立即返回。

4)如果锁被另一个线程保持,则出于线程调度目的,禁用当前线程,并且在发生以下两种情况之一以
前,该线程将一直处于休眠状态:
1)锁由当前线程获得;或者

2)其他某个线程中断当前线程。

5)如果当前线程获得该锁,则将锁保持计数设置为 1。
如果当前线程:
1)在进入此方法时已经设置了该线程的中断状态;或者

2)在等待获取锁的同时被中断。

则抛出 InterruptedException,并且清除当前线程的已中断状态。


6)在此实现中,因为此方法是一个显式中断点,所以要优先考虑响应中断,而不是响应锁的普通获取或
重入获取。

指定者: 接口 Lock 中的 lockInterruptibly
抛出: InterruptedException 如果当前线程已中断。



tryLock public boolean tryLock()

仅在调用时锁未被另一个线程保持的情况下,才获取该锁。

1)如果该锁没有被另一个线程保持,并且立即返回 true 值,则将锁的保持计数设置为 1。
即使已将此锁设置为使用公平排序策略,但是调用 tryLock() 仍将 立即获取锁(如果有可用的),
而不管其他线程当前是否正在等待该锁。在某些情况下,此“闯入”行为可能很有用,即使它会打破公
平性也如此。如果希望遵守此锁的公平设置,则使用 tryLock(0, TimeUnit.SECONDS)
,它几乎是等效的(也检测中断)。

2)如果当前线程已经保持此锁,则将保持计数加 1,该方法将返回 true。

3)如果锁被另一个线程保持,则此方法将立即返回 false 值。

指定者:
接口 Lock 中的 tryLock
返回:
如果锁是自由的并且被当前线程获取,或者当前线程已经保持该锁,则返回 true;否则返回
false


关于中断又是一段很长的叙述,先不谈。

1)lock(), 拿不到lock就不罢休,不然线程就一直block。 比较无赖的做法。
2)tryLock(),马上返回,拿到lock就返回true,不然返回false。 比较潇洒的做法。
带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false。比较聪明的做法。

3)lockInterruptibly()就稍微难理解一些。

先说说线程的打扰机制,每个线程都有一个 打扰 标志。这里分两种情况,
1. 线程在sleep或wait,join, 此时如果别的进程调用此进程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException;(thread在做IO操作时也可能有类似行为,见java thread api)

2. 此线程在运行中, 则不会收到提醒。但是 此线程的 “打扰标志”会被设置, 可以通过isInterrupted()查看并 作出处理。

lockInterruptibly()和上面的第一种情况是一样的, 线程在请求lock并被阻塞时,如果被interrupt,则“此线程会被唤醒并被要求处理InterruptedException”。并且如果线程已经被interrupt,再使用lockInterruptibly的时候,此线程也会被要求处理interruptedException


01.什么是显示锁 - 图1