名词解释
乐观悲观锁
在 Java 中几乎都是悲观锁, 仅存的乐观锁有Atomic(Integer/Boolean)
… 等原子类
实现乐观锁用到了 CAS(Compare And Swap: 比较并替换), 乐观锁本身并没有进行加锁操作, 只是不断循环重试的操作
-
保存原值为 A
-
执行对数据的操作
-
CAS: 对数据更新时, 更新为 B 之前检查原值是否为 A, 如果是就将原值更新为 B, 否则什么都不干, 这个操作对 CPU 来说是原子性的
乐观锁
适用于写比较少的场景, 可以省去锁的开销, 增大了整个系统的吞吐量. 但如果总是产生冲突, 会不断进行重试, 反倒降低了性能, 适合使用悲观锁
死锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
void thread0(int m) {
synchronized(lockA) { // 获得lockA的锁
synchronized(lockB) { // 获得lockB的锁
// todo
} // 释放lockB的锁
} // 释放lockA的锁
}
void thread1(int m) {
synchronized(lockB) { // 获得lockB的锁
synchronized(lockA) { // 获得lockA的锁
// todo
} // 释放lockA的锁
} // 释放lockB的锁
}
|
两个线程各自持有不同的锁,然后各自试图获取对方手里的锁,造成了双方无限等待下去,这就是死锁
解决办法: 线程获取锁的顺序要一致
可重入锁
1
2
3
4
5
6
7
|
void thread0(int m) {
synchronized(lockA) { // 获得lockA的锁 记录+1
synchronized(lockA) { // 记录+1
// todo
} // 释放lockA的锁 记录-1
} // 记录-1
}
|
JVM 允许同一个线程重复获取同一个锁
, 这种能被同一个线程反复获取的锁, 就叫做可重入锁, synchronized
和 ReentrantLock
都是可重入锁
Lock 类
ReentrantLock
表面翻译是可重入锁, 你可以把它当成手动控制的 synchronized
就比如下面的代码:
1
2
3
4
5
|
void add(int n) {
synchronized(this) {
// todo
}
}
|
可以替换为:
1
2
3
4
5
6
7
8
9
10
|
final Lock lock = new ReentrantLock();
void add(int n) {
lock.lock();
try {
// todo
} finally {
lock.unlock();
}
}
|
和synchronized
不同的是,ReentrantLock
可以尝试获取锁:
1
2
3
4
5
6
7
|
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// todo
} finally {
lock.unlock();
}
}
|
上述代码在尝试获取锁的时候,最多等待1秒。如果1秒后仍未获取到锁,tryLock()
返回false
,程序就可以做一些额外处理,而不是无限等待下去
公平锁
new 一个 ReentrantLock 的时候参数为 true, 表明实现公平锁机制, 谁等的时间最长, 谁就先获取锁
如果要实现随机获取, 可以传入参数 false(默认)
Condition
Condition
提供的await()
、signal()
、signalAll()
原理和synchronized
锁对象的wait()
、notify()
、notifyAll()
是一致的,并且其行为也是一样的
1
2
|
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
|
ReadWriteLock
- 只允许一个线程写入 (其他线程既不能写入也不能读取)
- 没有写入时, 多个线程允许同时读以提高性能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
final ReadWriteLock rwlock = new ReentrantReadWriteLock();
final Lock rlock = rwlock.readLock();
final Lock wlock = rwlock.writeLock();
void write(int index) {
wlock.lock(); // 加写锁
try {
// todo
} finally {
wlock.unlock(); // 释放写锁
}
}
int read() {
rlock.lock(); // 加读锁
try {
return 0;
} finally {
rlock.unlock(); // 释放读锁
}
}
|
ref: https://www.liaoxuefeng.com/