可重入锁 自旋锁,看这一篇就够了!

在多线程编程中,锁是经常使用地控制并发的机制,对于临界区的资源,须要保证线程之间互斥地访问。java

1. 可重入锁

可重入锁,也叫作递归锁,指的是屡次对同一个锁进行加锁操做,都不会阻塞线程。实现思路:记录当前锁正在被哪一个线程使用,采用计数来统计lock和unlock的调用次数。正常状况下,lock和unlock的调用次数应该相等,若是不相等就会死锁。面试

public class Test implements Runnable {
    ReentrantLock lock = new ReentrantLock(); //定义一个可重入锁

    public void get() {
        lock.lock(); //第一次调用lock()
        System.out.println(Thread.currentThread().getId());
        set();
        lock.unlock();
    }

    public void set() {
        lock.lock(); //第二次调用lock(),并且会成功,说明lock是可重入锁
        System.out.println(Thread.currentThread().getId());
        lock.unlock();
    }

    @Override
    public void run() {
        get();
    }

    public static void main(String[] args) {
        Test ss = new Test();
        new Thread(ss).start();
        new Thread(ss).start();
        new Thread(ss).start();
    }
}

2. 自旋锁

首先,看看初级的自旋锁实现方式:编程

public class SpinLock {
    private AtomicReference<Thread> owner =new AtomicReference<>();
    public void lock(){
        Thread current = Thread.currentThread();
        while(!owner.compareAndSet(null, current)){
        }
    }
    
    public void unlock (){
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }
}

实现思路:经过CAS(CompareAndSet)原子操做来更新变量。若是CAS返回true,表示得到了锁;不然,须要经过while循环检查,直到得到锁为止,这也是为何叫作自旋锁的缘由,须要不停的尝试获取锁。多线程

2.1 初级版本的问题
  1. 同一线程先后两次调用lock(),会致使第二次调用lock时进行自旋,产生了死锁(由于第一次调用lock()以后,尚未unlock),说明这个锁不是可重入的。
  2. 若是问题一已经解决,当第一次调用unlock()时,就已经将锁释放了。实际上不该释放锁。
2.2 解决方案
  1. 针对问题一:在lock函数内,应验证线程是否为已经得到锁的线程
  2. 针对问题二:采用计数进行统计
public class SpinLock {
    private AtomicReference<Thread> owner =new AtomicReference<>();
    private int count =0;
    public void lock(){
        Thread current = Thread.currentThread();
        if(current==owner.get()) {
            count++;
            return ;
        }

        while(!owner.compareAndSet(null, current)){
        }
    }
    
    public void unlock (){
        Thread current = Thread.currentThread();
        if(current==owner.get()){
            if(count!=0){
                count--;
            }else{
                owner.compareAndSet(current, null);
            }
        }
    }
}

3. 参考资料

  1. Java锁的种类以及辨析(四):可重入锁
  2. 面试必问的CAS,你懂了吗?
相关文章
相关标签/搜索