深入剖析Java中的锁【原理、锁优化、CAS、AQS】

1、悲观锁与乐观锁

广义概念,体现的是看待线程同步的不同角度。

悲观锁:悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,在获取数据的时候会先加锁,确保数据不会被别的线程修改。

锁实现:关键字synchronized、接口Lock(concurrent包下)的实现类

使用场景:写操作较多,先加锁可以保证写操作时数据正确。

乐观锁:乐观锁认为使用数据时不会有别的线程修改数据,所以不添加锁;只是在更新数据时去判断之前有没有其它线程更新了这个数据。

锁实现:CAS算法,例如AtomicInteger类的原子自增是通过CAS自旋实现

使用场景:读操作较多,不加锁的特点使读操作性能大幅提升。

1.1 悲观锁执行过程

阻塞 -> 唤醒,涉及上下文的切换(cpu时间片的切换,也即由一个线程切换到另一个线程执行)

1.2 乐观锁执行过程

不会进行加锁操作,所有线程同步竞争cpu的使用权。只会在更新数据的那个时刻,进行判断。

1.3 CAS算法

1.3.1 CAS自旋:

比较与交换算法:执行while循环,不断更新。

可以设定次数,若超过此阈值,则自旋结束;没有超过,自旋继续,直到更新成功为止。

自旋:重新load主内存中的V值到当前线程栈当中。

public final int getAndSetInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var4));

        return var5;
    }

 

1.3.2 基于CAS实现的原子类关键方法

1.3.3 CAS存在哪些问题

ABA问题:V的值实际上已经被线程3更改过了,但是线程2并不能感知到V的值的变化。

1.3.4 自旋锁

CPU运行原理:多线程任务都是使用cpu的一段时间片,每个时间片对应一个线程的处理任务,时间片的切换就是上下文的切换。

自旋:通过CAS算法,不断进行比较与更新,去读取主内存中最新的状态值;

线程的阻塞与唤醒,需要进行上下文的切换,影响性能,耗资源的操作。

自旋就是避免阻塞与唤醒。

通过自旋就可以并发修改更新成功,不断尝试自旋,没有必要进行阻塞。

自适应自旋:会根据上一次的自旋次数,上下浮动 调整下一次自旋的次数。

若达到自旋次数,还没有更新成功,则更新失败结束自旋,抛出异常挂起线程或返回。

1.4 synchronized分析 

注意:synchronized 关键字反编译的字节码指令是 monitorenter和 monitorexit,会被jvm识别,是否需要加锁与解锁。

1.4.1 Monitor

每个同步对象都有一个自己的Monitor(监视器锁)

1.4.2 JVM内置锁的膨胀升级

2、上下文切换

进程与线程上下文切换(消耗资源)原理解析?

  • cpu需要从用户态切换到内核态;同时上下文切换过程中,要保存上一个线程执行的中间变量指令指针写回到PCB进程控制块;再去切换到新的线程执行任务;当新线程时间片用完,又需要把上一个中断的线程的中间变量从内存条中读回,接着上次中断的状态执行。

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值