Java 中的各种锁
z 2020-04-27
Java
公平非公平锁,可重入锁,自旋锁,读写锁等。
# 公平锁
指根据线程在队列中的优先级获取锁,比如线程优先加入阻塞队列,那么线程就优先获取锁(火车站买票排队)
# 非公平锁
指在获取锁的时候,每个线程都会去争抢,并且都有机会获取到锁,无关线程的优先级(火车站买票可插队)
# 可重入锁
一个线程获取到锁后,如果继续遇到被相同锁修饰的资源或方法,那么可以继续获取该锁(允许多道锁,小区门口上锁,单元门上锁,厕所再上一把锁)
对 synchronized 来说,每个锁都有线程持有者和锁计数器,每次线程获取到锁,会记录下改线程,并且锁的计数器就+1,当线程退出synchronized代码块的时候,线程计数就会-1,当锁计数为0的时候,就释放锁。
# 自旋锁:
指当锁被获取后,其他线程并不会停止获取,而是一直去尝试获取(while + CAS实现的锁,各自线程不断地去看锁有没有释放)
public class SpinLock {
private static final AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void lock(){
//当前线程作为锁资源
Thread thread = Thread.currentThread();
//如果期盼值是null,也就是代表没有锁引用了,就设置为当前线程引用,如果不成功就while
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void unlock(){
//当前获取锁的线程
Thread thread = Thread.currentThread();
//释放锁,让下一个线程获取
atomicReference.compareAndSet(thread,null);
}
public static void main(String[] args) throws Exception{
SpinLock spinLock = new SpinLock();
new Thread(()->{
spinLock.lock();
//其他操作...
spinLock.unlock();
},"A").start();
}
}
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
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
这样做的好处是减少上下文开销,缺点是增加cpu消耗。
CAS底层就使用了自旋操作(不是自旋锁,而是如果预期值和原值比较不成功就会一直比较)
# 读写锁
独占锁:(一个坑只能有一个人拉屎)
锁一次只能被一个线程占有使用,Synchronized和ReetrantLock都是独占锁。
共享锁:(允许多个人同时看到那个牌子:厕所有人,正在拉屎。。)
锁可以被多个线程持有,对于ReentrantReadWriteLock而言,它的读锁是共享锁,写锁是独占锁 。
//模拟hibernate缓存被读和被写
public class ReadWriteLockDemo{
static class Cache{
//用map来充当缓存容器
private HashMap<String,Object> cache = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key , Object val){
readWriteLock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName()+" 开始写入");
cache.put(key,val);
System.out.println(Thread.currentThread().getName()+" 写入完成");
}
catch(Exception e){
e.printStackTrace();
}
finally{
readWriteLock.writeLock().unlock();
}
}
public void get(String key)
{
readWriteLock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName() + " 开始读取");
Object obj = cache.get(key);
System.out.println(Thread.currentThread().getName() + " 读取完成 : " + obj);
}
catch(Exception e){
e.printStackTrace();
}
finally{
readWriteLock.readLock().unlock();
}
}
}
public static void main(String[] args){
Cache cache = new Cache();
for (int i = 0 ; i < 5; ++i){
final int tempI = i;
new Thread(()->{
cache.put(String.valueOf(tempI),tempI);
}).start();
}
for (int i = 0 ; i < 5; ++i){
final int tempI = i;
new Thread(()->{
cache.get(String.valueOf(tempI));
}).start();
}
}
}
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 其他
- 刚发现美团写的不错 -- 传送门 (opens new window)