• 面试前必须要知道的【可重入锁 自旋锁】


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

    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,你懂了吗?
  • 相关阅读:
    逻辑即理性
    关于股市与经济的一点思考
    Objective-C Automatic Reference Counting (ARC)
    错误与异常
    Programming with Objective-C -- 属性的缺省实现
    视频容器格式与编码格式简介
    视频基础知识-封装格式和编码格式
    各种音视频编解码学习详解
    你的技术护城河是什么?--技术前沿代表了一个人的眼力和价值
    技术前沿--很多技术不了解细节也应该了解大概---知道能用来干什么--了解技术的价值
  • 原文地址:https://www.cnblogs.com/wengle520/p/12368689.html
Copyright © 2020-2023  润新知