美文网首页
1.多线程的目的及多线程原子性实现

1.多线程的目的及多线程原子性实现

作者: 陪伴你的大数据 | 来源:发表于2021-01-19 23:08 被阅读0次

    1.读线程的目的
    压榨多核CPU,多个CPU执行任务,比串行更快速
    2.多线程的的挑战
    1.上下文切换,线程切换影响执行速度
    1.1如何减少上下文切换
    减少线程--减少线程或使用协程
    减少锁--CAS、无锁编程(hash取模,不同线程执行不同段数据)
    1.2如何查看线程使用情况
    vmstate 1 每1s查看一次线程情况,cs参数可看到1s切换的次数。
    Lmbench3可以查看切换线程的时长
    2.死锁
    3.资源限制
    如果硬件资源有限制,网络、磁盘IO、连接数等都会限制多线程程序的执行。
    3.1解决资源限制
    池化技术--数据库连接池
    Socket复用
    任务连接数管控等
    3.2调参
    根据不同的资源限制,调整程序并发度。

    2.并发实现原理
    volatile、synchronized

    1.volatile原理与实现
    1.Lock指令会使处理器缓存写回内存。缓存锁定保证只有1个处理器缓存写回内存。
    2.一个处理器缓存写回内存,其他处理器缓存失效。嗅探技术保证内部缓存、系统内存、其他处理器缓存的数据一致性。如果有处理器写回内存,其他处理器缓存失效。
    1.2volatile的优化
    LinedTransferQueue,补充为64个字节,保证头结点、尾结点在1个高速缓存行。避免队列的出队、入队会频繁锁定头尾节点。
    没有必要补位的volatile情况
    1.缓存行非64位处理器
    2.共享变量不会被频繁地写,锁的几率小。

    2.synchronized原理与实现
    2.1synchronized锁定对象
    普通方法--当前实例对象
    静态方法--当前Class对象
    同步方法块---括号里的对象

    2.2Java对象头
    synchronized的锁存在对象头中,对象头的数组类型3个字宽,非数组2个字宽。
    有32bit和64bit两种。
    对象头包含对象的hashCode对象值、是否偏向锁、锁标志位,还可能有非带年龄

    2.3锁的升级与对比
    无锁、偏向锁、轻量级锁(自旋锁)、重量级锁(排它锁),只能升级不能降级。
    1.偏向锁--只对某一个线程
    上锁:在对象头和栈帧的锁记录里记录偏向的线程ID进行上锁。如果对象头的MarkWord中偏向锁标识=1,上锁成功,否则cas竞争锁。
    撤销锁:
    等到其他线程竞争才会让出锁的机制,到达没有指令执行的全局安全点时,拥有锁的线程会暂停,此时判断拥有锁的线程是否活着,如果死亡,对象头设置为无锁;如果活着,偏向锁的栈执行,遍历偏向对象的锁记录,栈中锁记录和对象头Mark Word要么重新偏向其他线程,要么恢复到无锁或者标记对象不适合偏向锁。
    关闭偏向锁:
    偏向锁需要启动JVM几秒后才能启用,J 6 J7 默认使用。关闭延迟
    -XX:BiasedLockingStartupDelay=0
    如果程序线程普遍存在竞争状态,则关闭偏向锁,直接升级为轻量级锁。
    -XX:UseBiasedLocking=flase
    2.轻量级锁
    上锁:在执行同步代码块之前,JVM在当前线程的栈帧中创建存储锁的空间,并将对象头MarkWord复制到栈帧锁记录中,称为Displace Mark Word。然后线程尝试使用CAS将对象头中的MarkWord锁记录指向栈帧锁记录的指针。cas失败,自旋获取。
    释放锁:cas操作将Displace MarkWrod替换回对象头。失败,膨胀为重量级锁。

    2.4偏向锁、轻量级锁、重量级锁的比较及应用场景。
    偏向锁 几乎不需要额外的消耗,只对对象中做标记,适合1个线程访问的同步快。多个线程竞争,会有锁撤销升级的消耗。
    轻量级锁:适用于非阻塞,需要响应速度快,同步代码执行速度快的场景。线程多,自旋多,cpu消耗变多。
    重量级锁:适用于追求吞吐量,同步代码快执行时间长,可阻塞的场景。不会自旋,消耗cpu时间

    3.原子操作实现原理
    原子操作---不可中断的一个或一系列操作。
    1.处理器的实现
    总线加锁+缓存加锁实现
    总线加锁:处理器提供一个LOCK#指令信号,将cpu和其他处理器缓存之间的通信锁住。只能有1个cpu核心读改写此共享变量。
    缓存加锁:总线加锁开销太大。当某些变量频繁使用会加载在处理器的L123等多级缓存中。处理器缓存写入到内存中,且在lock操作期间,修改内存中的缓存地址,由于缓存一致性协议,其他处理器关于此内存的变量缓存失效,其他处理器访问此变量时,重新从主内存中拉取。

    两种不支持缓存锁定
    1.访问的数据不能缓存在处理器内部或跨越多个缓存行,需要总线加锁。
    2.处理器不支持缓存锁,此时可以使用Lock前缀的各类指令实现。

    2.java中原子性
    1.CAS类
    cas通过处理器CMPXCHG指令,自旋cas操作,直到成功为止。轻量级锁、重量级锁及其他java中的锁都用cas来加锁和释放锁。
    cas类存在的3个问题:
    1.ABA问题
    同一个线程 a 改为b,又改为a,此时其他线程感觉没有变化,但实际变化了。增加版本号,Atomic包中提供了AtomicStampedReference解决ABA问题。
    2.cas自旋时间长,cpu开销大
    JVM如果支持处理器pause指令可有一定提升,可延迟流水线执行指令,同时避免在退出循环的时候顺序冲突引起的cpu了流水线清空,提升cpu效率。
    3.只能保证1个变量的原子操作。
    可使用拼接变量或AtomicReference类保证引用对象之间的原子性

    相关文章

      网友评论

          本文标题:1.多线程的目的及多线程原子性实现

          本文链接:https://www.haomeiwen.com/subject/rvtczktx.html