Lock

锁,多线程开发的基础,线程安全结构的基础,今天研究一下锁

说到多线程,我们还会想到synchronized关键字,锁和同步块相比,区别是什么?
先说结论,锁比同步关键字更实用,建议用锁

  1. Lock 是 interface, Synchronized 是关键字.
  2. Synchronized 自动释放锁, Lock 需要手动释放.
  3. Lock 在等待时可以被打断, 但是Synchronized 不可以,需要一直等待.
  4. Lock 明确知道是否有锁, 但是Synchronized 做不到.
  5. Synchronized 是CPU悲观锁机制,多线程时需要频繁切换CPU上下文,Lock是乐观锁机制,多线程是性能更高.
  6. Synchronized 可以用于class, methods, 和代码块, Lock可以用于代码逻辑
  7. Synchronized 不能实现公平锁, Lock 可以实现

Lock Interface

java.util.concurrent.locks.lock

常见实现

ReentrantLock

直接代码,来自网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class SharedObject {
//...
ReentrantLock lock = new ReentrantLock();
int counter = 0;

public void perform() {
lock.lock();
try {
// Critical section here
count++;
} finally {
lock.unlock();
}
}

public void performTryLock(){
//...
boolean isLockAcquired = lock.tryLock(1, TimeUnit.SECONDS);

if(isLockAcquired) {
try {
//Critical section here
count++;
} finally {
lock.unlock();
}
}
}
}

ReentrantReadWriteLock

读写锁,读等待写,写等待读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class SynchronizedHashMapWithReadWriteLock {

Map<String,String> syncHashMap = new HashMap<>();
ReadWriteLock lock = new ReentrantReadWriteLock();
// ...
Lock writeLock = lock.writeLock();

public void put(String key, String value) {
try {
writeLock.lock();
syncHashMap.put(key, value);
} finally {
writeLock.unlock();
}
}
public String remove(String key){
try {
writeLock.lock();
return syncHashMap.remove(key);
} finally {
writeLock.unlock();
}
}

Lock readLock = lock.readLock();
public String get(String key){
try {
readLock.lock();
return syncHashMap.get(key);
} finally {
readLock.unlock();
}
}

public boolean containsKey(String key) {
try {
readLock.lock();
return syncHashMap.containsKey(key);
} finally {
readLock.unlock();
}
}
}

StampedLock

Java 8 新引入的一个锁实现,可以返回stamp,用其解锁或检查锁是否还有效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class StampedLockDemo {
Map<String,String> map = new HashMap<>();
private StampedLock lock = new StampedLock();

public void put(String key, String value){
long stamp = lock.writeLock();
try {
map.put(key, value);
} finally {
lock.unlockWrite(stamp);
}
}

public String get(String key) throws InterruptedException {
long stamp = lock.readLock();
try {
return map.get(key);
} finally {
lock.unlockRead(stamp);
}
}

public String readWithOptimisticLock(String key) {
long stamp = lock.tryOptimisticRead();
String value = map.get(key);

if(!lock.validate(stamp)) {
stamp = lock.readLock();
try {
return map.get(key);
} finally {
lock.unlock(stamp);
}
}
return value;
}
}

Condition 接口

条件接口可以定义自己的条件约束,制定等待逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class ReentrantLockWithCondition {

Stack<String> stack = new Stack<>();
int CAPACITY = 5;

ReentrantLock lock = new ReentrantLock();
Condition stackEmptyCondition = lock.newCondition();
Condition stackFullCondition = lock.newCondition();

public void pushToStack(String item){
try {
lock.lock();
while(stack.size() == CAPACITY) {
stackFullCondition.await();
}
stack.push(item);
stackEmptyCondition.signalAll();
} finally {
lock.unlock();
}
}

public String popFromStack() {
try {
lock.lock();
while(stack.size() == 0) {
stackEmptyCondition.await();
}
return stack.pop();
} finally {
stackFullCondition.signalAll();
lock.unlock();
}
}
}

总结

还有一些细节没有深入研究。ConcurrentHashMap中同时看到了synchronized和ReentrantLock身影

下次应该总结一下Thread了吧