一.内存模型的相关概念
    

大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。

  也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。举个简单的例子,比如下面的这段代码:

i = i + 1;

当线程执行这个语句时,会先从主存当中读取i的值,然后复制一份到高速缓存当中,然后CPU执行指令对i进行加1操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中。

  这个代码在单线程中运行是没有任何问题的,但是在多线程中运行就会有问题了。在多核CPU中,每条线程可能运行于不同的CPU中,因此每个线程运行时有自己的高速缓存(对单核CPU来说,其实也会出现这种问题,只不过是以线程调度的形式来分别执行的)


例如:
       加入volatile 关键字 

private static volatile  boolean bChanged;

		public static void main(String[] args) throws InterruptedException {
			new Thread() {
				@Override
				public void run() {
					for (;;) {
						if (bChanged == !bChanged) {
							System.out.println("!=");
							System.exit(0);
						}
					}
				}
			}.start();
			Thread.sleep(1);
			new Thread() {

				@Override
				public void run() {
					for (;;) {
						bChanged = !bChanged;
					}
				}
			}.start();
		}
不加关键字 会有几率进入死循环
private static boolean bChanged;

		public static void main(String[] args) throws InterruptedException {
			new Thread() {
				@Override
				public void run() {
					for (;;) {
						if (bChanged == !bChanged) {
							System.out.println("!=");
							System.exit(0);
						}
					}
				}
			}.start();
			Thread.sleep(1);
			new Thread() {

				@Override
				public void run() {
					for (;;) {
						bChanged = !bChanged;
					}
				}
			}.start();
		}
1.volatile关键字的两层语义
  一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
  1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

  2)禁止进行指令重排序。

volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。