Skip to content

Commit 354e6f3

Browse files
committed
update readme in lock.
1 parent 51c8f66 commit 354e6f3

2 files changed

Lines changed: 138 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
- [transient](/keywords/transient.md)
1919
- [volatile](/keywords/volatile.md)
2020

21-
## [Java8](/java8)
21+
## [Java8](/java8)
2222

2323
## [Lock 篇](/lock)
2424

@@ -48,3 +48,4 @@
4848

4949
如果大家觉得不错,可以帮忙 star 或者 fork 下,谢谢!
5050

51+

lock/README.md

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,147 @@ Java 中以 Reentrant 开头命名的锁都是可重入锁,而且 **JDK 提供
160160

161161

162162
## 公平锁和非公平锁
163+
如果多个线程申请一把**公平锁**,那么获得锁的线程释放锁的时候,先申请的先得到,很公平。如果是**非公平锁**,后申请的线程可能先获得锁,是
164+
随机获取还是其它方式,都是根据实现算法而定的。
163165

164-
## 参数介绍
166+
对 ReentrantLock 类来说,通过构造函数可以**指定该锁是否是公平锁,默认是非公平锁**。因为在大多数情况下,非公平锁的吞吐量比公平锁的大,
167+
如果没有特殊要求,优先考虑使用非公平锁。
165168

166-
-XX:-UseBiasedLocking=false 关闭偏向锁
169+
而对于 synchronized 锁而言,它只能是一种非公平锁,没有任何方式使其变成公平锁。这也是 ReentrantLock 相对于 synchronized 锁的一个
170+
优点,更加的灵活。
171+
172+
以下是 ReentrantLock 构造器代码:
173+
174+
``` java
175+
176+
/**
177+
* Creates an instance of {@code ReentrantLock} with the
178+
* given fairness policy.
179+
*
180+
* @param fair {@code true} if this lock should use a fair ordering policy
181+
*/
182+
public ReentrantLock(boolean fair) {
183+
sync = fair ? new FairSync() : new NonfairSync();
184+
}
185+
186+
```
187+
188+
ReentrantLock 内部实现了 FairSync 和 NonfairSync 两个内部类来实现公平锁和非公平锁。具体源码分析会在接下来的章节给出,敬请关注
189+
该项目,欢迎 fork 和 star。
190+
191+
192+
## 可中断锁
193+
字面意思是"可以**响应中断**的锁"。
194+
195+
首先,我们需要理解的是什么是**中断**。 Java 中并没有提供任何可以直接中断线程的方法,只提供了**中断机制**。那么何为**中断机制**呢?
196+
线程 A 向线程 B 发出"请你停止运行"的请求,就是调用 Thread.interrupt() 的方法(当然线程 B 本身也可以给自己发送中断请求,
197+
即 Thread.currentThread().interrupt()),但线程 B 并不会立即停止运行,而是自行选择在合适的时间点以自己的方式响应中断,也可以
198+
直接忽略此中断。也就是说,Java 的**中断不能直接终止线程**,只是设置了状态为响应中断的状态,需要被中断的线程自己决定怎么处理。这就像
199+
在读书的时候,老师在晚自习时叫学生自己复习功课,但学生是否复习功课,怎么复习功课则完全取决于学生自己。
200+
201+
回到锁的分析上来,如果线程 A 持有锁,线程 B 等待持获取该锁。由于线程 A 持有锁的时间过长,线程 B 不想继续等了,我们可以让线程 B 中断
202+
自己或者在别的线程里面中断 B,这种就是 **可中段锁**
203+
204+
在 Java 中, synchronized 锁是**不可中断锁**,而 Lock 的实现类都是 **可中断锁**。从而可以看出 JDK 自己实现的 Lock 锁更加的
205+
灵活,这也就是有了 synchronized 锁后,为什么还要实现那么些 Lock 的实现类。
206+
207+
Lock 接口的相关定义:
208+
209+
```java
210+
211+
public interface Lock {
212+
213+
void lock();
214+
215+
void lockInterruptibly() throws InterruptedException;
216+
217+
boolean tryLock();
218+
219+
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
220+
221+
222+
void unlock();
223+
224+
Condition newCondition();
225+
}
167226

168227
```
169228

229+
其中 lockInterruptibly 就是获取可中断锁。
230+
231+
## 共享锁
232+
字面意思是多个线程可以共享一个锁。一般用共享锁都是在读数据的时候,比如我们可以允许 10 个线程同时读取一份共享数据,这时候我们
233+
可以设置一个有 10 个凭证的共享锁。
234+
235+
在 Java 中,也有具体的共享锁实现类,比如 Semaphore。 该类的源码分析会在后续章节进行分析,敬请关注该项目,欢迎 fork 和 star。
236+
237+
## 互斥锁
238+
字面意思是线程之间互相排斥的锁,也就是表明锁只能被一个线程拥有。
239+
240+
在 Java 中, ReentrantLock、synchronized 锁都是互斥锁。
241+
242+
## 读写锁
243+
读写锁其实是一对锁,一个读锁(共享锁)和一个写锁(互斥锁、排他锁)。
244+
245+
在 Java 中, ReadWriteLock 接口只规定了两个方法,一个返回读锁,一个返回写锁。
246+
247+
```java
248+
249+
public interface ReadWriteLock {
250+
/**
251+
* Returns the lock used for reading.
252+
*
253+
* @return the lock used for reading
254+
*/
255+
Lock readLock();
256+
257+
/**
258+
* Returns the lock used for writing.
259+
*
260+
* @return the lock used for writing
261+
*/
262+
Lock writeLock();
263+
}
264+
265+
```
266+
267+
文章前面讲过[乐观锁策略](#乐观锁的基础 --- CAS),所有线程可以随时读,仅在写之前判断值有没有被更改。
268+
269+
读写锁其实做的事情是一样的,但是策略稍有不同。很多情况下,线程知道自己读取数据后,是否是为了更改它。那么为何不在加锁的时候直接明确
270+
这一点呢?如果我读取值是为了更新它(SQL 的 for update 就是这个意思),那么加锁的时候直接加**写锁**,我持有写锁的时候,别的线程
271+
无论是读还是写都需要等待;如果读取数据仅仅是为了前端展示,那么加锁时就明确加一个**读锁**,其它线程如果也要加读锁,不需要等待,可以
272+
直接获取(读锁计数器加 1)。
273+
274+
虽然读写锁感觉与乐观锁有点像,但是**读写锁是悲观锁策略**。因为读写锁并没有在**更新前**判断值有没有被修改过,而是在**加锁前**决定
275+
应该用读锁还是写锁。乐观锁特指无锁编程。
276+
277+
JDK 内部提供了一个唯一一个 ReadWriteLock 接口实现类是 ReentrantReadWriteLock。通过名字可以看到该锁提供了读写锁,并且也是
278+
可重入锁。
279+
280+
## 总结
281+
Java 中使用的各种锁基本都是**悲观锁**,那么 Java 中有乐观锁么?结果是肯定的,那就是 java.util.concurrent.atomic 下面的
282+
原子类都是通过乐观锁实现的。如下:
283+
284+
``` java
285+
286+
public final int getAndAddInt(Object var1, long var2, int var4) {
287+
int var5;
288+
do {
289+
var5 = this.getIntVolatile(var1, var2);
290+
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
291+
292+
return var5;
293+
}
294+
295+
```
296+
297+
通过上述源码可以发现,在一个循环里面不断 CAS,直到成功为止。
298+
299+
## 参数介绍
300+
```
301+
-XX:-UseBiasedLocking=false 关闭偏向锁
302+
303+
170304
JDK1.6
171305
172306
-XX:+UseSpinning 开启自旋锁

0 commit comments

Comments
 (0)