点赞关注,不再迷路,你的支持对我意义重大!
🔥 Hi,我是丑丑。本文 「Java 路线」| 导读 —— 他山之石,可以攻玉 已收录,这里有 Android 进阶成长路线笔记 & 博客,欢迎跟着彭丑丑一起成长。(联系方式在 GitHub)
前言
- 锁是控制线程访问共享资源的方式
目录
1. 前置知识
这篇文章的内容会涉及以下前置 / 相关知识,贴心的我都帮你准备好了,请享用~
-
并发编程: 「Java 路线」| 并发编程的基础概念
-
AQS: 「Java 路线」| AQS 队列同步器
2. 锁的分类
2.1 隐式锁 & 显式锁
- 隐式锁:使用 synchronized 可以隐式地获取 / 释放锁,虽然具备便捷性,但是加锁 / 释放锁不具备可操作性;
- 显式锁:从 JDK 1.5 后,新增了 Lock 接口,赋予了加锁 / 释放锁更多可操作性与 synchronized 不具备的同步特性(见 第 3 节)。
2.2 悲观锁 & 乐观锁
区别在于线程是否需要锁住同步资源:
- 悲观锁:锁住;
- 乐观锁:不锁住。
2.3 公平锁 & 非公平锁
区别在于是否需要排队竞争锁:
- 公平锁:排队;
- 非公平锁:先尝试插队,插队失败再排队。
2.4 可重入锁 & 不可重入锁
区别在于线程获得锁之后是否可以再次获取锁:
- 可重入锁:可以,线程不阻塞;
- 非可重入锁:不可以,线程阻塞。
2.5 共享锁 & 排它锁
区别在于多个线程是否能共享同一个锁:
- 共享锁:能;
- 排他锁:不能。
3. Lock 接口
Lock 是 JDK 1.5 新增的锁接口,赋予了获得锁 / 释放锁更多可操作性与 synchronized 不具备的同步特性。
3.1 Lock 的使用范式
Lock 的使用范式要点如下:
Lock lock = new ReentrantLock();
lock.lock();
try{
do something ...
} finally {
lock.unlock();
}
- 1、在 finally{} 块中释放锁,以确保获得锁之后能够释放锁;
- 2、不要在 try{} 块中获得锁,避免在获取锁时发生异常,最后会无故调用释放锁。
3.2 Lock 和 synchronized 的区别
-
1、非阻塞地获取锁: 尝试获取锁,如果成功获取则返回 true,否则返回 false,而不是阻塞等待;
-
2、可中断地获取锁: 获取锁的线程能够响应中断;
-
3、超时获取锁: 可以指定获取锁的超时时间。
3.3 Lock 的 API
Lock 定义了获取锁 / 释放锁的基本操作,主要 API 如下:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long var1, TimeUnit var3) throws InterruptedException;
void unlock();
Condition newCondition();
}
方法 | 描述 |
---|---|
lock() | 获得锁 |
unlock() | 释放锁 |
tryLock() | 非阻塞地获取锁 |
lockInterruptibly() | 可中断地获取锁 |
tryLock(long,TimeUnit) | 超时获得锁,只有 3 种情况下会返回: 1、超时 2、获得锁 3、中断 |
4. AQS 队列同步器
锁是控制线程访问共享资源的方式,但是 Lock 只是一个接口,它是如何实现线程访问控制的呢?事实上,Lock 的实现基本都是通过聚合一个 AQS 队列同步器来实现线程访问控制的。关于 AQS 的内容,请看前置知识:
5. ReentrantLock 可重入锁
6. ReentrantReadWriteLock 读写锁
7. 并发工具类
7.1 CountDownLatch 等待多线程完成
7.2 CyclicBarrier 同步屏障
7.3 Symaphore 信号量
7.4 Exchanger 交换者
8. 总结
-
锁是控制线程访问同步资源的方式,在 JDK 1.5 之前,锁是通过 synchronized 关键字实现的;
-
Lock 是 JDK 1.5 新增的锁接口,赋予了加锁 / 释放锁更多可操作性与 synchronized 不具备的同步特性:非阻塞地获取锁、可中断地获取锁和超时获取锁。Lock 的实现基本是通过聚合一个 AQS 同步器来进行线程访问控制的;
创作不易,你的「三连」是丑丑最大的动力,我们下次见!
网友评论