Java中的各种锁

2020/4/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

这样做的好处是减少上下文开销,缺点是增加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

# 其他

此生不换
青鸟飞鱼