Java高并发程序-Chapter2 Java并行程序基础 (第六讲)volatile 与 Java 内存模型 JMM

时间:2022-08-28 18:01:05

Java内存模型都是围绕着原子性、有序性和可见性展开的

1. 原子性

指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰。


2. 可见性

指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。

显然,对于串行程序来说,可见性问题是不存在的。因为你在任何一个操作步骤中修改了某个变量,那么在后续的步骤中,读取这个变量的值,一定是修改后的新值

3. 有序性

在并发时,程序的执行可能就会出现乱序 ,需要做 指令重排,就是为了 尽量少的中断流水线
 
Java使用了一些特殊的操作或者关键字来申明、告诉虚拟机,在这个地方,要尤其注意,不能随意变动优化目标指令。
关键字 volatile就是“易变的,不稳定的”


当你用 volatile去申明一个变量时,就等于告诉了虚拟机,这个变量极有可能会被某些程序或者线程修改。

为了确保这个变量被修改后,应用程序范围内的所有线程都能够“看到”这个改动,虚拟机就必须采用一些特殊的手段,保证这个变量的可见性等特点


比如,根据编译器的优化规则,如果不使用 volatile申明变量,那么这个变量被修改后,其他线程可能并不会被通知到,甚至在别的线程中,看到变量的修改顺序都会是反的。但一旦使用 volatile,虚拟机就会特别小心地处理这种情况 


 1. Volatile保证操作的原子性

Java高并发程序-Chapter2 Java并行程序基础 (第六讲)volatile 与 Java 内存模型 JMM

保证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.

Java高并发程序-Chapter2 Java并行程序基础 (第六讲)volatile 与 Java 内存模型 JMM

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 ,程序无法退出

这个问题就是一个典型的可见性问题