Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 029713f

Browse files
Merge pull request Snailclimb#2554 from 1020325258/java-concurrent
[docs update]增加ReentrantLock相关内容
2 parents e196417 + 5357847 commit 029713f

File tree

1 file changed

+87
-1
lines changed

1 file changed

+87
-1
lines changed

‎docs/java/concurrent/java-concurrent-questions-02.md‎

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,16 +646,102 @@ public class SynchronizedDemo {
646646

647647
相比`synchronized`,`ReentrantLock`增加了一些高级功能。主要来说主要有三点:
648648

649-
- **等待可中断** : `ReentrantLock`提供了一种能够中断等待锁的线程的机制,通过 `lock.lockInterruptibly()` 来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情
649+
- **等待可中断** : `ReentrantLock`提供了一种能够中断等待锁的线程的机制,通过 `lock.lockInterruptibly()` 来实现这个机制。也就是说当前线程在等待获取锁的过程中,如果其他线程中断当前线程「 `interrupt()` 」,当前线程就会抛出 `InterruptedException` 异常,可以捕捉该异常进行相应处理
650650
- **可实现公平锁** : `ReentrantLock`可以指定是公平锁还是非公平锁。而`synchronized`只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。`ReentrantLock`默认情况是非公平的,可以通过 `ReentrantLock`类的`ReentrantLock(boolean fair)`构造方法来指定是否是公平的。
651651
- **可实现选择性通知(锁可以绑定多个条件)**: `synchronized`关键字与`wait()``notify()`/`notifyAll()`方法相结合可以实现等待/通知机制。`ReentrantLock`类当然也可以实现,但是需要借助于`Condition`接口与`newCondition()`方法。
652+
- **支持超时** :`ReentrantLock` 提供了 `tryLock(timeout)` 的方法,可以指定等待获取锁的最长等待时间,如果超过了等待时间,就会获取锁失败,不会一直等待。
652653

653654
如果你想使用上述功能,那么选择 `ReentrantLock` 是一个不错的选择。
654655

655656
关于 `Condition`接口的补充:
656657

657658
> `Condition`是 JDK1.5 之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个`Lock`对象中可以创建多个`Condition`实例(即对象监视器),**线程对象可以注册在指定的`Condition`中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用`notify()/notifyAll()`方法进行通知时,被通知的线程是由 JVM 选择的,用`ReentrantLock`类结合`Condition`实例可以实现"选择性通知"** ,这个功能非常重要,而且是 `Condition` 接口默认提供的。而`synchronized`关键字就相当于整个 `Lock` 对象中只有一个`Condition`实例,所有的线程都注册在它一个身上。如果执行`notifyAll()`方法的话就会通知所有处于等待状态的线程,这样会造成很大的效率问题。而`Condition`实例的`signalAll()`方法,只会唤醒注册在该`Condition`实例中的所有等待线程。
658659
660+
关于 **等待可中断** 的补充:
661+
662+
> `lockInterruptibly()` 会让获取锁的线程在阻塞等待的过程中可以响应中断,即当前线程在获取锁的时候,发现锁被其他线程持有,就会阻塞等待
663+
>
664+
> 在阻塞等待的过程中,如果其他线程中断当前线程 「 `interrupt()` 」,就会抛出 `InterruptedException` 异常,可以捕获该异常,做一些处理操作
665+
>
666+
> 为了更好理解这个方法,借用 Stack Overflow 上的一个案例,可以更好地理解 `lockInterruptibly()` 可以响应中断:
667+
>
668+
> ```JAVA
669+
> public class MyRentrantlock {
670+
> Thread t = new Thread() {
671+
> @Override
672+
> public void run() {
673+
> ReentrantLock r = new ReentrantLock();
674+
> // 1.1、第一次尝试获取锁,可以获取成功
675+
> r.lock();
676+
>
677+
> // 1.2、此时锁的重入次数为 1
678+
> System.out.println("lock() : lock count :" + r.getHoldCount());
679+
>
680+
> // 2、中断当前线程,通过 Thread.currentThread().isInterrupted() 可以看到当前线程的中断状态为 true
681+
> interrupt();
682+
> System.out.println("Current thread is intrupted");
683+
>
684+
> // 3.1、尝试获取锁,可以成功获取
685+
> r.tryLock();
686+
> // 3.2、此时锁的重入次数为 2
687+
> System.out.println("tryLock() on intrupted thread lock count :" + r.getHoldCount());
688+
> try {
689+
> // 4、打印线程的中断状态为 true,那么调用 lockInterruptibly() 方法就会抛出 InterruptedException 异常
690+
> System.out.println("Current Thread isInterrupted:" + Thread.currentThread().isInterrupted());
691+
> r.lockInterruptibly();
692+
> System.out.println("lockInterruptibly() --NOt executable statement" + r.getHoldCount());
693+
> } catch (InterruptedException e) {
694+
> r.lock();
695+
> System.out.println("Error");
696+
> } finally {
697+
> r.unlock();
698+
> }
699+
>
700+
> // 5、打印锁的重入次数,可以发现 lockInterruptibly() 方法并没有成功获取到锁
701+
> System.out.println("lockInterruptibly() not able to Acqurie lock: lock count :" + r.getHoldCount());
702+
>
703+
> r.unlock();
704+
> System.out.println("lock count :" + r.getHoldCount());
705+
> r.unlock();
706+
> System.out.println("lock count :" + r.getHoldCount());
707+
> }
708+
> };
709+
> public static void main(String str[]) {
710+
> MyRentrantlock m = new MyRentrantlock();
711+
> m.t.start();
712+
> }
713+
> }
714+
> ```
715+
>
716+
> 输出:
717+
>
718+
> ```BASH
719+
> lock() : lock count :1
720+
> Current thread is intrupted
721+
> tryLock() on intrupted thread lock count :2
722+
> Current Thread isInterrupted:true
723+
> Error
724+
> lockInterruptibly() not able to Acqurie lock: lock count :2
725+
> lock count :1
726+
> lock count :0
727+
> ```
728+
729+
关于 **支持超时** 的补充:
730+
731+
> **为什么需要 `tryLock(timeout)` 这个功能呢?**
732+
>
733+
> 假设这样一种场景:有一个加载缓存数据的任务在某个时间点多个线程同时要来执行,为了并发安全,通过锁来控制只有一个线程可以执行该任务。
734+
>
735+
> 假设大量线程同时来执行该任务,由于需要穿行执行,因此大量线程都进入阻塞队列等待获取锁
736+
>
737+
> 当第一个线程拿到锁,执行完任务之后,此时后边的线程都不需要执行该任务了,但是由于没有这个超时功能,导致后边的线程还需要在队列中阻塞等待获取锁,再一个个进入同步代码块,发现任务已经执行过了,不需要自己再执行了,之后再退出释放锁,退出同步代码块。
738+
>
739+
> 因此就需要一个支持超时的功能,`tryLock(timeout)` 的作用就是 **将大量线程的串行操作转为并行操作** ,当大量线程等待时间已经超过了指定的超时时间,直接返回 false,表示获取锁失败,不需要大量的线程串行排队等待获取锁。
740+
>
741+
> ![image-20241208153800259](https://11laile-note-img.oss-cn-beijing.aliyuncs.com/image-20241208153800259.png)
742+
>
743+
> 这里 `tryLock(timeout)` 的情况只是举一个特殊的情况,其实是参考了分布式环境下,更新 Redis 缓存时会出现这种情况,但是在分布式环境下肯定不会使用 synchronized ,因此这里主要是举个例子说一下 tryLock(timeout) 的作用!
744+
659745
### 可中断锁和不可中断锁有什么区别?
660746
661747
- **可中断锁**:获取锁的过程中可以被中断,不需要一直等到获取锁之后 才能进行其他逻辑处理。`ReentrantLock` 就属于是可中断锁。

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /