并发编程原理

任务调度

进程让操作系统实现了并发,而线程让进程内部实现了并发

线程安全的本质

(1) 原子性
(2) 有序行 —> 编译器的重排序和CPU的指令重排序
(3) 可见性 —> 一个线程对共享变量的修改对另外一个线程不可见

线程的状态及状态的切换

6种状态

  • new
  • runnable(就绪、运行)
  • blocked(阻塞)
    (a) 等待阻塞
    (b) 同步阻塞
    © 其它阻塞
  • (4) waiting
  • (5) time waiting
  • (6) terminated

如何中断一个线程

interrupt(中断) —> 会使线程抛出 InterruptException中断异常
isInterrupt()方法判断是否被中断
Thread.interrupted(); //复位,之后isInterrupt()方法将变为false

volatile原理

  • 概述

    • 并发编程中的三个概念:

      • 原子性:即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
      • 可见性:可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
      • 有序性:即程序执行的顺序按照代码的先后顺序执行。
    • volatile 可以保证可见性、防止指令重排序(解决了可见性、有序性问题)

    • synchronized 解决了原子性、可见性、有序性问题

    • volatile/synchronized —> Lock指令

      • 把当前处理器缓存行的数据写回系统内存
      • 使得CPU内的缓存地址失效
  • CPU 级别的缓存一致性原理
    为了解决缓存一致性问题CPU通过两种锁的途径来解决: 总线锁和缓存锁
    缓存一致性LOCK

    • 总线锁(全局锁)
      是指变量从共享内存获取到某颗CPU独有内存后,其它CPU核心再次获取时会被阻塞住,直到释放锁(相当于悲观锁)性能比较低,现代CPU架构绝大多数都是用缓存锁的方式来实现的
    • 缓存锁(锁定缓存行)
      X86架构 MESI协议(缓存一致性协议)
    简写 英文 中文
    M modified 修改
    E exclusive 独占
    S Shared 共享
    I Invalid 失效

    (1) 读取count变量到缓存中,我们认为当前状态是Share

    (2)当其中一个CPU核心修改了count值,状态会先变成独占E

    • 设置成E(独占)使得其它核心中的线程获取count值时,获取不到
    • 设置成M后对数据做修改,修改后通过嗅探协议通知其它CPU核心缓存的count值失效,并将count值由CPU核心写回主内存
    • 另外一个CPU核心接到count缓存失效的通知后,将状态变为I (invalid),再次从主内存重新获取count值
  • 指令重排序问题
    指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。

    • 内存屏障
      (1) 是CPU或者编译器对内存的一个随机访问操作的同步点
      (2) 就是在两个操作中间加了一个屏障(或者说同步的点),该屏障之前所有的指令全部执行完毕, 屏障之后的指令能够读取到屏障前指令的结果
    • 内存屏障有三种
      (a) store barrier //写屏障
      (b) load barrier //读屏障
      (c ) full barrier //全屏障 —> 具有写屏障和读屏障的功能(综合体)
    • 内存屏障的作用
      (1) 保证可见性
      (2) 保证有序性
      内存屏障是CPU层面的东西,Java无法直接触及,Java通过JMM来间接触及内存屏障
  • JMM规范
    Java Memory Model —> Java内存模型
    定义的是线程和内存的交互方式

  • volatile的代码使用

join原理

在很多情况下,主线程创建并启动了线程,如果子线程中要进行大量耗时运算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。方法join()的作用是等待线程对象销毁。这里有一个很值得注意的问题,join的底层调用的是wait方法,而且是循环调用,源码如图所示:

我们可以看到源码中join方法会在while循环中一直调用wait方法,假如wait的时间是1000ms,在500ms的时候另外一个线程调用了notifyAll方法时,线程就会苏醒。

更多详情请见

https://blog.csdn.net/madongyu1259892936/article/details/79230924 线程基础-volatile关键字
https://www.cnblogs.com/yanlong300/p/8986041.html 并发研究之CPU缓存一致性协议(MESI)
https://blog.csdn.net/beiyetengqing/article/details/49580559 多线程之指令重排序

发布了133 篇原创文章 · 获赞 11 · 访问量 8706

猜你喜欢

转载自blog.csdn.net/xuanyuanjiaqi/article/details/105157483