您的位置:首页 > 编程语言 > Java开发

Java多线程中的内存模型

2018-02-10 15:04 316 查看

Java多线程中的内存模型

1)多线程运行下的内存模型:
JVM中规定了所有变量都存储在主内存中,然后每条线程有自己的工作内存。
线程的工作内存中保存了该线程需要用到的变量的拷贝值,线程在CPU上运行时都是对自己工作线程中的数据进行读写操作,
运行结束之后才把数据同步化主内存中。那么数据一致性协议、数据一致性问题,JVM中就需要线程同步机制来达到多线程对同一内存区域的读写控制了。

此外,Java编译器为了提高性能,采取了 指令重排序(类比计算机的 乱序执行),
若多个线程都有语句对同一内存区域进行操作的话,有可能因为指令重排序而导致结果不符预料。
因此,也需要线程同步机制来达到多线程对同一内存区域的读写控制。

2)主内存与工作内存的数据交互
JVM定义了8种操作来完成主内存与线程工作内存的数据交互:
       1:lock:把主内存变量标识为一条线程独占,此时不允许其他线程对此变量进行读写。
       2:unlock:解锁一个主内存变量。
       3:read:把一个主内存变量值读入到线程的工作内存,强调的是读入这个过程。
       4:load:把read到变量值保存到线程工作内存中作为变量副本,强调的是读入的值的保存过程。
       5:use:线程执行期间,把工作内存中的变量值传给字节码执行引擎。
       6:assign(赋值):字节码执行引擎把运算结果传回工作内存,赋值给工作内存中的结果变量。
       7:store:把工作内存中的变量值传送到主内存,强调传送的过程。
       8:write:把store传送进来的变量值写入主内存的变量中,强调保存的过程。
JVM要求以上8个操作都具有原子性,即对数据的读写操作具有原子性。但也有例外,即:long、double的非原子性协定:这两个64位类型的数据的读、写操作各需两次进行,一次读/写 32 位,这两次读/两次写 是不保证原子性的。

3)原子性、可见性、有序性
原子性:基本数据类型的读写操作是原子性的;更大范围的(代码块)的原子性可以用lock、unlock操作来实现(上锁后就只有 一个线程来执行了,所以不会被其他线程打断原子操作),表现到代码层面就是使用syncrhoized同步块。

可见性:当一个线程修改了被多线程共享的一个主内存变量值时,其他线程能立刻知道这个修改。
          我们在上面可以知道,JVM是通过工作内存中的变量值变化后,把新值同步会主内存,然后其他线程从主内存读取这                个新值来实现可见性的。这里有个区别:普通变量的值变化后,不一定会立刻同步会主内存,而是会等线程执行完或者             一段时间后才同步会,而且同步回主内存后,其他线程的工作内存也不一定会立刻读取新值。而被volatile关键字修饰            的变量,一旦在工作内存中被修改,则立刻同步回主内存,并且其他使用了这个变量的线程的工作内存会立刻从主内存            读取新值。而syncrhoized关键字修饰的变量由于一次只能有一个线程能使用,故一次也只能有一个工作线程读写它,            所以也能“纵向”地实现可见性。

有序性:多线程之间对共享数据的操作的有序性,可以通过volatile和syncrhoized关键字来保证。volatile关键字禁止了指令               重排序,而syncrhoized关键字规定了多个线程每次只能有一个线程对共享数据进行操作。

4)volatile关键字

一个volatile变量具有两种特性:可见性、禁止指令重排序。但是,volatile不具备原子性!原因是volatile变量的值可以被多线程交替修改,而修改包括了read、load、use、store、write等过程,这些过程不能保证是原子执行的。
可见性:被volatile关键字修饰的变量,一旦在工作内存中被修改,则立刻同步回主内存,并且其他使用了这个变量的线程的工作内存会立刻从主内存读取新值。

禁止指令重排序:volatile变量在赋值后会创建一个内存屏障:指令重排序时,位于后面的指令不能排到内存屏障之前。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: