GitHub 4.1k Star 的Java工程师成神之路 ,不来了解一下吗?java
GitHub 4.1k Star 的Java工程师成神之路 ,真的不来了解一下吗?git
GitHub 4.1k Star 的Java工程师成神之路 ,真的肯定不来了解一下吗?程序员
最近,面试过不少Java中高级开发,问过不少次关于Java内存模型的知识,问完以后,不少人上来就开始回答:github
Java内存模型由几部分组成,堆、本地方法栈、虚拟机栈、方法区...面试
每一次我不想打断他们的话,虽然我知道这又是一个误会了个人问题的朋友。算法
其实,我想问的Java内存模型,是和并发编程有关的。而候选人给我回答的那叫JVM内存结构,彻底是两回事。编程
不少时候,在我没有打断他们的状况下,一部分人慢慢的讲到了GC相关的知识。这种状况下,我只能硬着头皮继续问一些和JVM有关的知识。缓存
可是,个人本意实际上是想看一下他对Java并发有多少了解啊。多线程
常常,我都在继续追问了一些他们回答的"Java内存模型"相关的知识后,友善的提醒一句,其实我想问的Java内存模型并非他回答的这个...并发
有的时候,我会进一步提醒一句:是和并发编程有关的,是和主内存以及线程工做内存有关的。。。
那么,本文就来简单说一说,关于Java内存模型,到底应该如何回答这个面试题。
首先,咱们先来分析一下问什么不少人,甚至是大多数人会答非所问呢?
我以为主要有几个缘由:
一、Java内存模型,这个词听着太像是关于内存分布的知识了。听上去和并发编程没有半毛钱关系。
二、网上不少资料都是错的。不信你去网上搜索一下"Java内存模型",你会发现,不少人打着内存模型的标题,介绍了JVM内存结构的知识。
这里提一句,我尝试着Google搜索了一下搜索"Java内存模型",首页展现结果以下:

首页排名靠前的5篇文章中,有1篇是错的,介绍了JVM内存结构。
PS:值得庆幸的的是,首页前5篇文章中,有两篇是我写的,至少个人这两篇我敢肯定是不具有任何误导性的!!
三、还存在一种状况,虽然很少见,可是也有。那就是不少面试官本身也觉得内存模型就是要介绍堆、栈、方法区这些知识。就致使有时候面试者不知道本身到底应该如何回答。
那么,到底什么是Java内存模型?关于这道面试题应该如何回答呢?
我曾经在《再有人问你Java内存模型是什么,就把这篇文章发给他》中详细的介绍过Java内存模型的前因后果,这里再从新回顾一下。
Java内存模型是根据英文Java Memory Model(JMM)翻译过来的。其实JMM并不像JVM内存结构同样是真实存在的。他只是一个抽象的概念。
Java内存模型的相关知识在 JSR-133: Java Memory Model and Thread Specification 中描述的。JMM是和多线程相关的,他描述了一组规则或规范,这个规范定义了一个线程对共享变量的写入时对另外一个线程是可见的。
Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各类硬件和操做系统的访问差别的,保证了Java程序在各类平台下对内存的访问都能获得一致效果的机制及规范。目的是解决因为多线程经过共享内存进行通讯时,存在的原子性、可见性(缓存一致性)以及有序性问题。
那么,咱们这里就先来讲说什么是所谓的内存模型规范、这里提到的原子性、可见性以及有序性又是什么东西?
原子性
线程是CPU调度的基本单位。CPU有时间片的概念,会根据不一样的调度算法进行线程调度。因此在多线程场景下,就会发生原子性问题。由于线程在执行一个读改写操做时,在执行完读改以后,时间片耗完,就会被要求放弃CPU,并等待从新调度。这种状况下,读改写就不是一个原子操做。即存在原子性问题。
缓存一致性
在多核CPU,多线程的场景中,每一个核都至少有一个L1 缓存。多个线程访问进程中的某个共享内存,且这多个线程分别在不一样的核心上执行,则每一个核心都会在各自的caehe中保留一份共享内存的缓冲。因为多核是能够并行的,可能会出现多个线程同时写各自的缓存的状况,而各自的cache之间的数据就有可能不一样。
在CPU和主存之间增长缓存,在多线程场景下就可能存在缓存一致性问题,也就是说,在多核CPU中,每一个核的本身的缓存中,关于同一个数据的缓存内容可能不一致。
有序性
除了引入了时间片之外,因为处理器优化和指令重排等,CPU还可能对输入代码进行乱序执行,好比load->add->save 有可能被优化成load->save->add 。这就是有序性问题。
多CPU多级缓存致使的一致性问题、CPU时间片机制致使的原子性问题、以及处理器优化和指令重排致使的有序性问题等,都硬件的不断升级致使的。那么,有没有什么机制能够很好的解决上面的这些问题呢?
最简单直接的作法就是废除处理器和处理器的优化技术、废除CPU缓存,让CPU直接和主存交互。可是,这么作虽然能够保证多线程下的并发问题。可是,这就有点因噎废食了。
因此,为了保证并发编程中能够知足原子性、可见性及有序性。有一个重要的概念,那就是——内存模型。
为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操做行为的规范。经过这些规则来规范对内存的读写操做,从而保证指令执行的正确性。它与处理器有关、与缓存有关、与并发有关、与编译器也有关。他解决了CPU多级缓存、处理器优化、指令重排等致使的内存访问问题,保证了并发场景下的一致性、原子性和有序性。
针对上面的这些问题,不一样的操做系统都有不一样的解决方案,而Java语言为了屏蔽掉底层的差别,定义了一套属于Java语言的内存模型规范,即Java内存模型。
Java内存模型规定了全部的变量都存储在主内存中,每条线程还有本身的工做内存,线程的工做内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的全部操做都必须在工做内存中进行,而不能直接读写主内存。不一样的线程之间也没法直接访问对方工做内存中的变量,线程间变量的传递均须要本身的工做内存和主存之间进行数据同步进行。
而JMM就做用于工做内存和主存之间数据同步过程。他规定了如何作数据同步以及何时作数据同步。

了解Java多线程的朋友都知道,在Java中提供了一系列和并发处理相关的关键字,好比volatile、synchronized、final、concurren包等。其实这些就是Java内存模型封装了底层的实现后提供给程序员使用的一些关键字。
在开发多线程的代码的时候,咱们能够直接使用synchronized等关键字来控制并发,历来就不须要关心底层的编译器优化、缓存一致性等问题。因此,Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。
本文并不许备把全部的关键字逐一介绍其用法,由于关于各个关键字的用法,网上有不少资料。读者能够自行学习。本文还有一个重点要介绍的就是,咱们前面提到,并发编程要解决原子性、有序性和一致性的问题,咱们就再来看下,在Java中,分别使用什么方式来保证。
原子性
在Java中,为了保证原子性,提供了两个高级的字节码指令monitorenter和monitorexit。在synchronized的实现原理文章中,介绍过,这两个字节码,在Java中对应的关键字就是synchronized。
所以,在Java中可使用synchronized来保证方法和代码块内的操做是原子性的。
可见性
Java内存模型是经过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存做为传递媒介的方式来实现的。
Java中的volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后能够当即同步到主内存,被其修饰的变量在每次是用以前都从主内存刷新。所以,可使用volatile来保证多线程操做时变量的可见性。
除了volatile,Java中的synchronized和final两个关键字也能够实现可见性。只不过实现方式不一样,这里再也不展开了。
有序性
在Java中,可使用synchronized和volatile来保证多线程之间操做的有序性。实现方式有所区别:
volatile关键字会禁止指令重排。synchronized关键字保证同一时刻只容许一条线程操做。
好了,这里简单的介绍完了Java并发编程中解决原子性、可见性以及有序性可使用的关键字。读者可能发现了,好像synchronized关键字是万能的,他能够同时知足以上三种特性,这其实也是不少人滥用synchronized的缘由。
可是synchronized是比较影响性能的,虽然编译器提供了不少锁优化技术,可是也不建议过分使用。
前面我介绍完了一些和Java内存模型有关的基础知识,只是基础,并非所有,由于随便一个知识点仍是均可以展开的,如volatile是如何实现可见性的?synchronized是如何实现有序性的?
可是,当面试官问你:能简单介绍下你理解的内存模型吗?
首先,先和面试官确认一下:您说的内存模型指的是JMM,也就是和并发编程有关的那一个吧?
在获得确定答复后,再开始介绍(若是不是,那可能就要回答堆、栈、方法区哪些了....囧...):
Java内存模型,实际上是保证了Java程序在各类平台下对内存的访问都可以获得一致效果的机制及规范。目的是解决因为多线程经过共享内存进行通讯时,存在的原子性、可见性(缓存一致性)以及有序性问题。
除此以外,Java内存模型还提供了一系列原语,封装了底层实现后,供开发者直接使用。如咱们经常使用的一些关键字:synchronized、volatile以及并发包等。
回答到这里就能够了,而后面试官可能会继续追问,而后根据他的追问再继续往下回答便可。
因此,当有人再问你Java内存模型的时候,不要一张嘴就直接回答堆栈、方法区甚至GC了,那样显得很不专业!