Java内存模型都是围绕着原子性、有序性和可见性展开的
1. 原子性
指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰。
2. 可见性
指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。显然,对于串行程序来说,可见性问题是不存在的。因为你在任何一个操作步骤中修改了某个变量,那么在后续的步骤中,读取这个变量的值,一定是修改后的新值
3. 有序性
在并发时,程序的执行可能就会出现乱序 ,需要做 指令重排,就是为了 尽量少的中断流水线Java使用了一些特殊的操作或者关键字来申明、告诉虚拟机,在这个地方,要尤其注意,不能随意变动优化目标指令。
关键字 volatile就是“易变的,不稳定的”
当你用 volatile去申明一个变量时,就等于告诉了虚拟机,这个变量极有可能会被某些程序或者线程修改。
为了确保这个变量被修改后,应用程序范围内的所有线程都能够“看到”这个改动,虚拟机就必须采用一些特殊的手段,保证这个变量的可见性等特点
比如,根据编译器的优化规则,如果不使用 volatile申明变量,那么这个变量被修改后,其他线程可能并不会被通知到,甚至在别的线程中,看到变量的修改顺序都会是反的。但一旦使用 volatile,虚拟机就会特别小心地处理这种情况
1. Volatile保证操作的原子性
保证t操作 即使在32位系统中, 也是原子操作的。
2. Volatile并不能代替锁,它也无法保证一些复合操作的原子性
例如:
package com.john.learn.high.concurent.ch02.volatileJmm; public class PlusTask { private volatile static int count = 0; public static class Plus implements Runnable { @Override public void run() { for (int i = 0; i < 1000; i++) { count++; } } } public static void main(String[] args) throws InterruptedException { Thread[] threads = new Thread[10]; Plus plus = new Plus(); for (int i = 0; i < threads.length; i++) { threads[i] = new Thread(plus); threads[i].start(); } for (int i = 0; i < threads.length; i++) { threads[i].join(); } System.out.println("Total count:"+PlusTask.count); } }
static int count =0;
多线程 count++ 假设 10000次 ,最终结果小于10000.
3. 保证数据的可见性和有序性
public class NoVisibility { private static int number; private volatile static boolean ready; private static class ReaderThread extends Thread { @Override public void run() { while (!ready) ; System.out.println(ready); System.out.println(number); } } public static void main(String[] args) throws InterruptedException { ReaderThread readerThread = new ReaderThread(); readerThread.start(); Thread.sleep(1000); number = 42; ready = true; Thread.sleep(1000); } }
在主线程修改 ready变量的状后, Reader Thread可以发现这个改动,并退出程序。
如果 private volatile static boolean ready; 去掉volatile ,程序无法退出
这个问题就是一个典型的可见性问题