结合时间片调度的java自旋锁分析

自旋锁

互斥锁: 在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象

自旋锁: 自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,即在标志寄存器中关闭/打开中断标志位,不需要自旋锁)。

以上是百度百科中对于互斥锁与自旋锁的定义.

在java中, 常见的互斥锁有关键字synchronized, 有Lock类, 有继承AQS的类

而自旋锁与互斥锁不同, 它不存在互斥量, 在互斥锁争夺锁的过程中, 未获得锁的线程往往都是被阻塞的, 等待被获取锁的线程处理完毕自身任务后唤醒

而自旋锁则不同, 使用了自旋锁的代码片段, 线程在未获得锁的时候不会放弃cpu, 而是进入一个忙循环,在循环中会判断是否达到某一条件, 常常与cas操作一起使用

cas

cas的全称是compare and swap, 即比较并交换, cas在cpu底层是一条命令构成的, 所以是原子性的, 在java内存模型中, 线程是无法直接操作主内存的, 线程只能改写自己的工作内存然后将工作内存中的信息刷回主内存中, 线程与线程间共享的信息也只能通过主内存共享.

所以这里就出现了, 当A线程与B线程同时读取变量i并进行累加操作的时候, 并发问题就出现了, 变量i可能被加了一次, 也可能被加了两次, 这个取决于线程读取到的变量是刷回的变量还是原先主内存中的变量

在java中cas操作往往伴随着Unsafe类来直接对内存进行操作, 并且与volatile共同使用.

比较工作内存中的变量与主内存中变量是否一致, 如果一致则直接修改替换. 这一系列操作在底层原语中是一条指令, 所以是线程安全的. 这保证了只有一个线程可以对一个变量进行比较修改操作

而volatile关键字修饰的变量发生改变后, 会导致其他线程的工作内存中该变量失效, 因此其他线程需要读取该变量值的时候需要从主内存中重新读取

所以cas+volatile变量可以实现一个上锁的功能

下面是AtomicInteger类中的自增方法

	private volatile int value;    
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
	public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        // 循环cas判断
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

可以发现这是一个cas结合volatile变量的方法

自旋锁的问题分析

  1. 操作系统有自己的调度逻辑, 当该线程的时间片用完之后, 自旋锁的意义何在

这是之前面试中, 由线程池讲到自旋锁的时候面试官问我的一个问题.

这里分析一下在这个情境下的互斥锁与自旋锁

互斥锁: 在互斥条件下, 线程无法获取锁会将自己挂起阻塞, 而cpu是不会分配时间片给阻塞态的线程的. 所以这个线程没有获取锁而阻塞的时候, 这个时间片会直接结束, 并且cpu运行下一个就绪线程, 当阻塞的线程被唤醒后, 会进入就绪队列等待时间片的分配,

自旋锁: 当一个线程开始自旋, 如果此时时间片用尽, 则cpu会运行下一个就绪线程, 一直直到该线程重新获取cpu时间片, 他依然还在自旋, 如果不设置超时时间, 他会继续耗尽时间片进行下一轮等待, 如果一直获取不到锁则会一直循环等待消耗cpu资源

所以说自旋锁适用于运行时间短并且锁竞争不那么激烈的情形, 这样不会消耗太多的cpu资源,

而互斥阻塞的上锁方法则会增加线程上下文的切换时间, 如果持有锁的时间非常短暂, 而需要获取锁的线程又将自己挂起了, 那么cpu将时间片分配给了其他线程, 这会导致其实锁早就被释放了但是没有线程去获取,代码块执行时间长的一个假象(此时所有线程都在等待时间片), 这也会导致程序运行过慢

所以如果想要一段运行时间短的并发代码的效率提升, 自旋锁是一个很好的选择

以上就是结合了操作系统调度与自旋锁的简单分析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值