什么是 CAS(自旋锁)? 它的优缺点? 如何使用CAS实现一把锁? | 您所在的位置:网站首页 › 自旋锁的实现 › 什么是 CAS(自旋锁)? 它的优缺点? 如何使用CAS实现一把锁? |
什么是自旋锁?
CAS 没有获取到锁的线程是不会阻塞的,通过循环控制一直不断的获取锁。 CAS: Compare and Swap,翻译成比较并交换。 执行函数 CAS(V,E,N) CAS 有 3 个操作数,内存值 V,旧的预期值 E,要修改的新值 N。当且仅当预期值 E 和内存值 V 相同时,将内存值 V 修改为 N,否则什么都不做 原子类: AtomicBoolean,AtomicInteger,AtomicLong 等使用 CAS 实现。 优缺点优点:没有获取到锁的线程,会一直在用户态,不会阻塞,没有锁的线程会一直通过循环控制重试。 缺点:通过死循环控制,消耗 cpu 资源比较高,需要控制循次数,避免 cpu 飙高问题。 CAS实现一把锁逻辑Cas 无锁机制原理: 定义一个锁的状态;状态状态值=0 则表示没有线程获取到该锁;状态状态值=1 则表示有线程已经持有该锁;实现细节: CAS 获取锁: 将该锁的状态从 0 改为 1-----能够修改成功 cas 成功则表示获取锁成功 如果获取锁失败–修改失败,则不会阻塞而是通过循环(自旋来控制重试) CAS 释放锁: 将该锁的状态从 1 改为 0 如果能够改成功 cas 成功则表示释放锁成 完整代码 /** * @author zyz * @version 1.0 * @data 2023/7/17 15:09 * @Description: 使用CAS实现一把锁 * 测试 * 1、上锁不使用循环实现 * 1):获取锁、释放锁, 结果有些获取成功,有些获取失败 * 2):获取锁,不释放锁 结果:只有一个线程获取锁成功,其它都失败 * 2、上锁使用循环实现 * 1) 获取锁、释放锁, 全部成功。 * 2) 获取锁,不释放锁, 结果:只有一个线程获取成功,其它线程一直循环等待。CPU飙高 */ public class UsingCasHandwritingLock { private AtomicLong cas = new AtomicLong(0); private Thread lockCurrentThread; /** * 获取锁 * 锁是有状态的 * 0 --- 表示没有人持有该锁 * 1 --- 表示该锁已经被线程持有 * 获取成功:cas 0变为1 cas = true * 获取锁失败 cas false */ public Boolean tryLock() { boolean result = cas.compareAndSet(0, 1); return result; } /** *:优点:没有获取到锁的线程,会一直在用户态,不会阻塞,没有锁的线程会一直通过循环控制重试。 * 缺点:通过死循环控制,消耗 cpu 资源比较高,需要控制循次数,避免 cpu 飙高问题; * @return */ // public Boolean tryLock(){ // while (true){ // System.out.println(Thread.currentThread().getName() + ",CAS"); // boolean result = cas.compareAndSet(0,1); // if(result){ // lockCurrentThread = Thread.currentThread(); // return true; // } // } // } /** * 释放锁 */ public Boolean unLock() { if (lockCurrentThread != Thread.currentThread()) { return false; } return cas.compareAndSet(1, 0); } public static void main(String[] args) { UsingCasHandwritingLock lock = new UsingCasHandwritingLock(); IntStream.range(1, 10).forEach((i) -> new Thread(() -> { try { boolean result = lock.tryLock(); if (result) { lock.lockCurrentThread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + ",获取锁成功~~~"); } else { System.out.println(Thread.currentThread().getName() + ",获取锁失败~~~"); } } catch (Exception ex) { } finally { if (lock != null) { lock.unLock(); } } }).start()); } } 测试结果 1、释放锁(非循环获取锁) //获取锁 public Boolean tryLock() { boolean result = cas.compareAndSet(0, 1); return result; }正常释放锁 try { //做的任务 } catch (Exception ex) { //抛异常 } finally { //释放锁 if (lock != null) { lock.unLock(); } }结果:有些线程获取锁成功,有些线程获取锁失败。 说明:线程获取到该锁,并且还在执行任务,未到释放锁的时候。其它线程在获取锁的时候,就会出现获取锁失败的情况。 将释放锁的过程取消,获取到该锁的线程一直拿着。 try { //做的任务 } catch (Exception ex) { //抛异常 } finally { //释放锁 // if (lock != null) { // lock.unLock(); // } }结果:只有一个线程获取锁成功,其它线程获取锁均失败 说明:某个线程获取到该锁,并且一直拿着,不释放。其它线程就不能获取到锁。 当前线程执行完任务后,释放锁 try { //做的任务 } catch (Exception ex) { //抛异常 } finally { //释放锁 if (lock != null) { lock.unLock(); } }结果:线程全部获取到锁。 说明:未获取到锁的线程,通过循环的形式一直尝试获取锁。只要有线程释放了锁。其它线程就去尝试获取锁,获取不到锁的线程,就一直循环尝试获取。直到获取到锁为止。 线程执行完任务后,不释放锁 try { //做的任务 } catch (Exception ex) { //抛异常 } finally { //释放锁 // if (lock != null) { // lock.unLock(); // } }效果:只有一个线程获取到锁,其它线程一直循环尝试获取锁。 说明:当一个线程获取到锁之后,其它线程就一直循环尝试获取锁。但是该线程没有释放锁,就会导致其它线程一直循环尝试获取锁,就会导致CPU飙高。 |
CopyRight 2018-2019 实验室设备网 版权所有 |