• volitale最经典理解


    • volatile跟Java的内存模型有关,非volatile变量时,平常情况,线程执行时会将变量从主内存加载到线程工作内存,建立一个副本,在某个时刻写回。
    • valatile指的每次都读取主内存的值,有更新则立即写回主内存。
    1. “volatile保证了线程之间的可见性”:因为线程看到volatile变量会去读取主内存最新的值,而不是自个一直在那跟内部的变量副本玩,所以保证了valatile变量在各个线程间的可见性。
      1. “volatile保证了线程之间的可见性”:因为线程看到volatile变量会去读取主内存最新的值,而不是自个一直在那跟内部的变量副本玩,所以保证了valatile变量在各个线程间的可见性。
        1. “volatile保证了线程之间的可见性”:因为线程看到volatile变量会去读取主内存最新的值,而不是自个一直在那跟内部的变量副本玩,所以保证了valatile变量在各个线程间的可见性
          1. “volatile保证了线程之间的可见性”:因为线程看到volatile变量会去读取主内存最新的值,而不是自个一直在那跟内部的变量副本玩,所以保证了valatile变量在各个线程间的可见性。
    作者:陈美芳
    链接:https://zhuanlan.zhihu.com/p/28324074
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    我们都知道线程是在寄存器中运行的,而变量是保存在内存中的,当线程需要使用普通变量时会把变量copy一份到寄存器中,然后进行(多次)使用、修改等操作,完成之后再将更新后的变量写到内存中。但是在线程对变量副本进行修改等操作时,内存中变量的变化对于该线程是不可见的,这种行为是线程不安全的。笔者截取了一小段代码为例来说明这个问题:

    public class CThread extends Thread {
        private OutputStream ous;
        static  boolean flag=true;
    
    	public CThread(OutputStream ous) {
    		super();
    		this.ous = ous;
    	}
    
    
    	@Override
    	public void run(){
    		while(flag){
    			Scanner c=new Scanner(System.in);
    			try {
    				ous.write((c.nextLine()+"
    ").getBytes());
    				ous.flush();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    
    	}
    
    
    }
    

    在主函数中启动线程后将flag的值修改为false,线程很有可能继续运行。这是因为主函数中对变量flag的操作对线程不可见。即线程已经拷贝了一份flag的信息(拷贝时flag为true),然后在寄存器里进行一系列操作,当内存更新后线程不知道,仍然以副本中flag的值(true)在运行。

    而volatile修饰的变量具有可见性,即保证线程读取到的是最新更新的值。线程不拷贝内存变量而是直接读取内存中的变量,当内存中变量被其他线程修改后线程能立马知道。volatile是比同步更轻量级的操作,同步是锁定变量,只允许一个线程对其进行操作,是原子性动作。

    当然被volatile修饰的变量也不是绝对的线程安全的。

    public class Test extends Thread{
    	private volatile static  int count=0;
    	 public static void main(String[] args) {
    		 Test t= new Test();
    		 t.test();
    		 System.out.println(count);
    	 }
    
    	 public void test(){
    
    		 for(int i=0;i<1000;i++){
    			 new Test().start();
    		 }
    
    	 }
    
    	 @Override
    	 public void run(){
    		 try {
    			Thread.sleep(100);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
    			count++;
    	}
    
    }
    

    我运行的结果是:999

    为什么不是理论上的1000呢?

    假设count在内存中最新更新的值是666,才是a线程和b线程都读取了这个变量的值然后分别进行加1的操作,a更新内存的值为667,b也更新内存的值为667,这就导致了线程不安全。

  • 相关阅读:
    spring mvc配置完后实现下载功能
    表单中Readonly和Disabled的区别(转载)
    EL表达式中fn函数 (转载)
    Spring mvc中@RequestMapping 6个基本用法小结(转载)
    web开发,关于jsp的常见问题,重复提交,防止后退。
    JQuery页面加载
    解决 spring mvc 3.0 结合 hibernate3.2 使用<tx:annotation-driven>声明式事务无法提交的问题(转载)
    数据库设计原则(转载)
    dhtmlxTree介绍(转载)
    主键索引
  • 原文地址:https://www.cnblogs.com/panxuejun/p/8622194.html
Copyright © 2020-2023  润新知