java并发编程学习理解(一)

并发编程可能会遇到的问题

  1. 多线程一定快吗

    • 这是不一定的,比如我的任务非常简单,但是我分成多个线程来实现,那么多线程的执行时间可能是远远大于单线程的,因为这回多出很多额外的上下文切换的时间
    • 再者,在资源受限的情况下,肯定还是无法达到多线程想要的效果,比如我一个cpu,即使我把任务做成几十个线程,那么其实还是串行执行的,反而多了很多上下文切换的花销。
  2. 所以为了为了避免我们的上下文切换的花销

    • 我们应该无锁并发编程,比如将数据的id按照hash算法取模分段,不同的线程处理不同的数据
    • 最少线程:避免创建不必要的线程。造成大量线程处于等待状态。
    • cas:不需要加锁,牺牲一定的cpu时间,来换取避免进程切换的时间。
    • 协程:在单线程内首先多个任务的调度和切换。

并发编程底层实现原理

  • volatile和synchrinized是并发编程中两个重要的角色。
  1. volatile
    • 可见性:为了提高处理速度,我们的处理器一般不直接与内存通信,因为他们的处理速度差异很大,因此是将数据加载到告诉缓存中来进行操作,那么就会导致一个数据可见性的问题,即一个线程修改了数据,但是这个修改可能修改了缓存中的值,还未修改内存中的值,其他处理器不知道这个值修改了,这是就是可见性。为了解决这个问题,可是使用volatile关键字,使用这个关键字后,如果查看翻译的汇编语言会发现多了一个lock指令,这个指令会将缓存行写回到内存中(此时出现缓存一致性问题),并且其他处理器回通过嗅探的方式得知这个内存地址做了修改,使得当前缓存行里的数据失效,因此重新加载这个内存地址的数据到缓存行中。实现了可见性。
    • 核心就是两个点:(1)当前处理器数据写回内存(2)写回操作会使得对应的其他处理器的缓存失效。
  2. synchronized的实现原理和应用
    • 对象头
      • 在java中,任何对象都可以作为锁,这是因为在对象头中存储了锁标记信息。依赖于对所标记信息的操作,可以实现一系列的对锁的操作。对象头中主要存储有三部分信息,(1)markword(2)类的元信息地址(3)如果是数组对象,那么还存储了数组的长度。
      • 我们主要看markwork,其中存储了hashcode,分代年龄,和锁标记信息。这些数据是随着锁标志位的变化而变化。
    • 偏向锁的加锁和锁撤销
      • 当一个线程执行同步块的时候,会首先检查对应锁的标志位,如果是01,并且是否偏向是否的话,就直接使用cas操作获得偏向锁。如果是01,但是已偏向,则检查是否是当前线程,若是则直接获得偏向锁,若不是则尝试使用cas操作去获取锁,如果失败(在threadid不为空或者不是本身id时会失败),(偏向锁所用出现竞争才释放的机制)则去检查持有锁的线程是否存活,如果不存活,则直接将是否偏向位置为0,再使用cas去获取锁。如果存活,则需要去遍历线程栈中的lockword,如果线程任然持有锁,则需要对锁进行升级成轻量级锁。
      • 如果我们确定程序的锁在大多数情况下都处于竞争状态,那么我们可以关闭偏向锁。
    • 轻量级
      • 轻量级锁的加锁解锁及升级
        • 在尝试获得一个轻量级锁的时候,线程会在线程栈中开辟一块空间lock record复制锁的markword,被成为dispalcedmarkword并使用cas来获取锁,将锁的markword替换为这个线程lock record的指针。表示后的锁。释放锁也是使用cas机制(这里很不理解,其他线程也使用cas来替换,就算锁已经被获得,别的线程也会替换成功吧,因为这段时间锁的原先markword位置的值是获得锁的线程lockrecord的指针
        • 释放锁也是cas机制。
        • 未获得锁的线程进行自旋等待获得锁,自旋一定次数(有算法,比如根绝之前的自旋次数)后或等待线程多与一定的数量后会升级成为重量级锁。
  • 38
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值