zoukankan      html  css  js  c++  java
  • Java高并发4-解析volatile关键字

    一、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
    • 这结果和我们预想的不一样,为什么呢?下次再说

    二、源码:

  • 相关阅读:
    linux centos 7.5 开启 postgresql 远程访问
    linux centos 7 开启 ftp
    CentOS 7.5 改IP后不生效无法上网解决办法
    Windows Server 2008R2 及上系统安装 Windows 可选功能
    C#只允许运行一个实例
    C# 命令行参数分割
    C# 获取所有已登录系统的用户名
    C#获取进程用户名
    psexec 用法
    检测 Visual C++ Redistributable Package 相应版本是否已安装
  • 原文地址:https://www.cnblogs.com/ruigege0000/p/13923531.html
Copyright © 2011-2022 走看看