科技报道|一次性搞清楚,Java并发编程在各主流框架中的应用,保证看懂

Spring、Netty、Mybatis 等框架的代码中大量运用了 Java 多线程编程技巧 。 并发编程处理的恰当与否 , 将直接影响架构的性能 。本文通过对这些框架源码的分析 , 结合并发编程的常用技巧 , 来讲解多线程编程在这些主流框架中的应用 。
Java 内存模型JVM 规范定义了 Java 内存模型来屏蔽掉各种操作系统、虚拟机实现厂商和硬件的内存访问差异 , 以确保 Java 程序在所有操作系统和平台上能够达到一致的内存访问效果 。
工作内存和主内存
Java 内存模型规定所有的变量都存储在主内存中 , 每个线程都有自己独立的工作内存 , 工作内存保存了对应该线程使用的变量的主内存副本拷贝 。线程对这些变量的操作都在自己的工作内存中进行 , 不能直接操作主内存和其他工作内存中存储的变量或者变量副本 。 线程间的变量传递需通过主内存来完成 , 三者的关系如下图所示 。
科技报道|一次性搞清楚,Java并发编程在各主流框架中的应用,保证看懂Java 内存操作协议
Java 内存模型定义了 8 种操作来完成主内存和工作内存的变量访问 , 具体如下 。
科技报道|一次性搞清楚,Java并发编程在各主流框架中的应用,保证看懂

  • read:把一个变量的值从主内存传输到线程的工作内存中 , 以便随后的 load 动作使用 。
  • load:把从主内存中读取的变量值载入工作内存的变量副本中 。
  • use:把工作内存中一个变量的值传递给 Java 虚拟机执行引擎 。
  • assign:把从执行引擎接收到的变量的值赋值给工作内存中的变量 。
  • store:把工作内存中一个变量的值传送到主内存中 , 以便随后的 write 操作 。
  • write:工作内存传递过来的变量值放入主内存中 。
  • lock:把主内存的一个变量标识为某个线程独占的状态 。
  • unlock:把主内存中 一个处于锁定状态的变量释放出来 , 被释放后的变量才可以被其他线程锁定 。
内存模型三大特性1、原子性
这个概念与事务中的原子性大概一致 , 表明此操作是不可分割 , 不可中断的 , 要么全部执行 , 要么全部不执行 。 Java 内存模型直接保证的原子性操作包括 read、load、use、assign、store、write、lock、unlock 这八个 。
2、可见性
可见性是指当一个线程修改了共享变量的值 , 其他线程能够立即得知这个修改 。Java 内存模型是通过在变量修改后将新值同步回主内存 , 在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的 , 无论是普通变量还是 volatile 变量都是如此 , 普通变量与 volatile 变量的区别是 , volatile 的特殊规则保证了新值能立即同步到主内存 , 以及每次使用前立即从主内存刷新 。 因此 , 可以说 volatile 保证了多线程操作时变量的可见性 , 而普通变量则不能保证这一点 。 除了 volatile 外 , synchronized 也提供了可见性 , synchronized 的可见性是由 “对一个变量执行 unlock 操作 之前 , 必须先把此变量同步回主内存中(执行 store、write 操作)” 这条规则获得 。
3、有序性
单线程环境下 , 程序会 “有序的”执行 , 即:线程内表现为串行语义 。 但是在多线程环境下 , 由于指令重排 , 并发执行的正确性会受到影响 。 在 Java 中使用 volatile 和 synchronized 关键字 , 可以保证多线程执行的有序性 。 volatile 通过加入内存屏障指令来禁止内存的重排序 。 synchronized 通过加锁 , 保证同一时刻只有一个线程来执行同步代码 。
volatile 的应用打开 NioEventLoop 的代码中 , 有一个控制 IO 操作 和 其他任务运行比例的 , 用 volatile 修饰的 int 类型字段 ioRatio , 代码如下 。


推荐阅读