概述

JVM如何实现多线程、多线程之间由于共享和和竞争数据而导致的一系列问题及解决方案

  • 多任务处理
    在许多场景下,让计算机同时去做几件事情
    1.因为计算机的运算能力强大
    2.计算机的运算速度比存储和通信速度大多了,太多的时间花费在磁盘I/O、网络通信或者数据库上

除要充分利用计算机处理器能力,一个服务端要同时对多个客户端提供服务。

衡量一个服务性能的高低好坏,每秒事务处理数(Transactions Per Second,TPS)是重要的指标,代表着1s内服务端平均能响应的请求总数,而TPS值与程序的并发能力又有非常密切的关系。
对于计算量相同的任务,程序线程并发协调得越有条不紊,效率自然就会越高;反之,线程之间频繁争用数据,互相阻塞甚至死锁,将会大大降低程序的并发能力。

服务端的应用是Java语言最擅长的领域之一,如何写好并发应用程序是服务端程序开发的难点之一,处理好并发方面的问题通常需要更多的编码经验支持。
各种中间件服务器、各类框架也都努力地替程序员隐藏尽可能多的线程并发细节,使得程序员在编码时能更关注业务逻辑,而不是花费大部分时间去关注此服务会同时被多少人使用、如何处理数据争用、协调硬件资源。

硬件的效率与一致性

由于计算机的存储设备与处理器的运算速度有着几个数量级的差距,所以现代计算机系统都不得不加入一层或多层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲:将运算需要使用的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步到内存之中,这样处理器就无需等待缓存的内存读写了

缓存一致性问题

在多路处理系统中,每个处理器都有自己的高速缓存,而它们又共享同一主内存(Main Memory),这种系统称为共享内存多核系统(Shared Memory Multiprocessors System)。当多个吹起的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致
image.png

解决办法

需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作,常见协议有MSI、MESI(Illinois Protocol)、MOSI、Synapse、Firefly及Dragon Protocol等

为了使处理器内部运算单元能尽量被充分利用,处理器可能会对代码进行乱序执行优化,处理器会在计算之后将乱序执行的结果重组,保证结果与顺序执行的结果是一致的,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致,因此如果一个计算任务依赖另外一个计算任务的中间结果,那么其顺序写并不能靠代码的先后顺序来保证

Java内存模型

Java内存模型屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果
在此之前,主流程序语言(C、C++)直接使用物理硬件和操作系统的内存模型。由于不同平台上内存模型的差异,可能导致程序在一套平台上并发完全正常,而在另一套平台上并发访问却经常出错

主内存与工作内存

Java内存模型的主要目的是定义程序中各种变量的访问规则,即关注在虚拟机中把变量值存储到内存和从内存中取出变量值这样的底层细节

  • 规则
    所有变量都存储在主内存中(仅仅是JVM内存的一部分)。
    每条线程有自己的工作内存,线程内的工作内存保存了被该线程使用的变量的主内存副本,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的数据。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者的交互关系如下图
    image.png

内存间交互操作

即一个变量从主内存拷贝到工作内存、从工作内存同步回主内存
Java内存模型定义了8中操作来完成,每一种操作都是原子性、不可再分的(double、long类型变量在某些平台上有例外)

8种操作

  • lock锁定
    作用于主内存的变量,把一个变量标识为一条线程独占的状态
  • unlock解锁
    作用于主内存的变量,把一个锁定状态的变量释放出来,可以被其他线程锁定
  • read读取
    作用于主内存的变量,吧一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load载入
    作用于工作内存的变量,把read操作从主内存中得到的变量值放入工作内存的变量副本中
  • use使用
    作用于工作内存的变量,把工作内存中变量值传递给执行引擎
  • assign赋值
    作用于工作内存的变量,把从执行引擎接受的值付给工作内存中的变量
  • store存储
    作用于工作内存的变量,把工作内存中变量值传送到主内存
  • write写入
    作用域主内存的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中

这个家伙很懒,啥也没有留下😋