zoukankan      html  css  js  c++  java
  • 并发编程基础之volatile关键字的用法

    一:概念

    volatile关键字是一个轻量级的线程同步,它可以保证线程之间对于共享变量的同步,假设有两个线程a和b,

    它们都可以访问一个成员变量,当a修改成员变量的值的时候,要保证b也能够取得成员变量最新的值,程序的

    内存模型是这样的,程序运行时,成员变量的值被加载到内存中,如果线程a运行时,会把变量的值拷贝到cpu分配

    给a的高速缓存区,就是内存的一个副本,线程b运行时,会把变量拷贝到cpu分配给b的高速缓存区,正常情况下,

    a线程修改成员变量时,会将高速缓存中的值写入主存,然后b线程运行时读取主存中值到缓存,但是不是强制性的,

    使用volatile关键字就是强制性。

    1:将高速缓存强制写入主内存

    2:会使b线程高速缓存标记失效

    二:比较经典的一个示例

    t1线程先启动,然后一直打印‘i love u’,这时t2线程启动,将flag变量的值修改为true,然后t1线程的执行终止,如果flag变量不加volatile修饰,

    出现死循环的概率是存在的,但是比较低,如果加volatile,会强制t2线程修改主内存中flag的值,而且t1线程高速缓存标记会失效,可以保证

    一定能够终止t1程序的执行

    /**
     * 
     */
    package com.day2;
    
    /**
     * @author Administrator
     *
     */
    public class ListAdd1 {
    	
    	private boolean flag;
    	
    	public static void main(String[] args) {
    		ListAdd1 list = new ListAdd1();
    		
    		//线程1
    		Thread t1 = new Thread("t1"){
    			public void run(){
    				while(!list.flag){
    					System.out.println("i love u");
    				}
    			}
    		};
    		
    		//线程2
    		Thread t2 = new Thread("t2"){
    			public void run(){
    				list.flag = true;
    			}
    		};
    		
    		t1.start();
    		//保证t1线程先启动
    		try {
    			Thread.sleep(100);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		t2.start();
    	}
    	
    	
    }
    
    private volatile boolean flag;
    

      

    但是volatile并不能保证操作的原子性,线程抢到cpu的时间片,修改高速缓存的值,写入主内存这几个过程不是原子的,

    int i = 0;

    i = i+1;

    如果线程1在抢到cpu的时间片之后,还没有修改高速缓存的值,然后线程2也读取了主内存中缓存的值i = 0,然后执行加1,

    写入高速缓存,线程1之前读取缓存中的值也是0,然后执行加1,写入主内存,这样就出现问题了,所以使用volatile不能

    保证线程安全问题。

    如下示例:

    启动10个线程,count初始值为0,正常情况,10个线程个循环1000次,最后的count值应该为10000,但是不是,这个值

    是随机的。

    /**
     * 
     */
    package com.day2;
    
    /**
     * @author Administrator
     *
     */
    public class ListAdd2 {
    	
    	private volatile int count;
    	
    	public static void main(String[] args) {
    		ListAdd2 list = new ListAdd2();
    		
    		System.out.println(list.count);
    		
    		for(int i=0;i<10;i++){
    			new Thread("t"+i){
    				public void run(){
    					for(int j=0;j<1000;j++){
    						list.count++;
    					}
    				}
    			}.start();
    		}
    		
    		System.out.println(list.count);
    	}
    	
    	
    }
    

      

    如果想确保线程安全,那么必须使用synchronized锁

    synchronized (list) {
    							list.count++;
    						}
    

     

    因为10个线程访问的是同一个实例,所以使用对象锁就可以了。

  • 相关阅读:
    JavaScript中字符串处理的一些函数
    JavaScript中的call、apply、bind方法的区别
    JavaScript中的数组与伪数组的区别
    关于字符集和字符编码那些事
    JavaScript的技巧和最佳实践
    Linux下编辑利器vim,vimrc,viminfo的高级用法
    pdo,更高的sql安全性
    Centos下cacti的安装
    nginx中的502错误
    mac下webpagetest搭建
  • 原文地址:https://www.cnblogs.com/warrior4236/p/7531973.html
Copyright © 2011-2022 走看看