一、volatile解析

1.计算机内部的内存模型

  • 我们知道计算机内部含有内存和CPU,那么在进行计算的时候,内存读写还是太慢了,因此在内存和CPU之间还是有一个缓存cache
  • 那么我们知道如果是一个共享变量的话,就会导致,内存中变量和缓存中的变量由于多线程同步不及时,也就是说,一个线程中的缓存还没来的急写入内存,此时有可能在内存中的变量被其他线程读取了。
  • 解决这个问题可以:1)通过在总线加LOCK#锁的方式;2)通过缓存一致性协议
  • 第一种方式效率低下,很难实现多线程;第二种方式有一个著名MSI协议,就是指当缓存写入内存之后,会向其他线程发出信号,共享变量数值已变,如果需要的话,需要重新更新。
    4.1

2.并发编程的三个概念

  • 原子性:一组操作要么全部执行成功,要么全部执行失败,不存在部分成功,部分失败的情况
  • 可见性:一个线程对共享变量在缓存中已经更改,但是还没有来得及写入内存,此时又有一个线程访问了该变量,那么就会出现数据不一致
  • 有序性:各个语句的执行其实不一定按照从上到下的顺序,但是最后个结果一定和按从上到下执行的结果一致,因为如果语句不相干,先执行哪个后执行哪个无所谓,但是语句相干,就会按照从上到下的正常顺序。

3.Java中的内存模型

  • 与计算机的内存模型相似,也会有三大特性
  • 原子性,这个特性,可以通过synchronnized和lock来保证
  • 可见性,Java通过violate来保证,如果共享变量是有violate来修饰那么就是保证,如果改变了它的值就能保证绝对会写入内存之后,别的线程才会调用。
  • 有序性,Java中存在happens-before原则来保证有序性,如果两条语句通过该原则仍然不能推断先后执行的先后顺序,那么这两个语句是不是有序的,谁先执行都说不准。
  • 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
  • (1)程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
  • (2)锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作
  • (3)volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
  • (4)传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
  • (5)线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
  • (6)线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
  • (7)线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
  • (8)对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始

4.下面我们举个例子

package com.ruigege.threadFoundation1;

public class TestViolate {
	
	public volatile int inc =1;
	
	
	public static void main(String[] args) {
		TestViolate test = new TestViolate();
	
		for(int i=0;i<10;i++) {
			//创建十个线程,对inc进行自增操作
			Thread thread = new Thread(new Runnable() {
				@Override
				public void run() {
					for(int j=0;j<10000;j++) {
						test.inc++;
					}
				}
			});
			thread.start();
		}
		
		while(Thread.activeCount()>1) {
			Thread.yield();//如果存在还没有结束的线程,就需要尽量让出CPU供它们运行
		}
		System.out.println(test.inc);
	}
	
}
  • 我们将这段代码运行三次
    4.2
    4.3
    4.4
  • 这结果和我们预想的不一样,为什么呢?下次再说

二、源码:

posted on 2020-11-04 00:19  心悦君兮君不知-睿  阅读(243)  评论(0编辑  收藏  举报