zoukankan      html  css  js  c++  java
  • 【JUC系列第一篇】-Volatile关键字及内存可见性

    作者:毕来生
    微信:878799579

    1. 什么是JUC?

    JUC全称 java.util.concurrent 是在并发编程中很常用的实用工具类

    2.Volatile关键字

    1、如果一个变量被volatile关键字修饰,那么这个变量对所有线程都是可见的。
    2、如果某条线程修改了被Volatile修饰的这个变量值,修改后的值对于其他线程来时是立即可见的。
    3、并不是经过Volatile修饰过的变量在多线程下就是安全的
    4、多线程间可以使用SynchronousQueue或者Exchanger进行数据之间传递

    3.内存可见性

    内存可见性(Memory Visibility)是指当某个线程正在使用对象状态 而另一个线程在同时修改该状态,需要确保当一个线程修改了对象 状态后,其他线程能够看到发生的状态变化。
    可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。
    原理同CAS原理相同,不懂的同学可以自行百度,附上一张CAS演示图供大家参考

    在这里插入图片描述

    4.实战举例

    通过线程来修改变量count的值,使用Volatile关键字修饰和不使用Volatile修饰count变量结果对比。

    首先我们来看一下通过内部类实现Runnable,变量使用Volatile关键字修饰演示以及结果

    package org.bilaisheng.juc;
    
    /**
     * @Author: bilaisheng
     * @Wechat: 878799579
     * @Date: 2019/1/1 16:29
     * @Todo: 通过内部类实现Runnable,变量使用Volatile关键字修饰演示
     * @Version : JDK11 , IDEA2018
     */
    public class NoVolatileTest{
    
    	public static void main(String[] args) {
    		NoVolatileThread noVolatileThread = new NoVolatileThread();
    		new Thread(noVolatileThread).start();
    
    		while (true){
    			if(noVolatileThread.isFlag()){
    				System.out.println("flag 此时为true !");
    				break;
    			}
    		}
    	}
    }
    
    class NoVolatileThread implements Runnable{
    
    	private boolean flag = false;
    	
    	@Override
    	public void run() {
    		try {
    			Thread.sleep(500);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
            
    		flag = true;
    
    		System.out.println(Thread.currentThread().getName() + " flag = " + flag);
    	}
    
    	public boolean isFlag() {
    		return flag;
    	}
    
    	public void setFlag(boolean flag) {
    		this.flag = flag;
    	}
    }
    

    运行结果如下图所示:

    在这里插入图片描述


    接下来我们来看一下通过内部类实现Runnable,变量不使用Volatile关键字修饰演示以及结果

    package org.bilaisheng.juc;
    
    /**
     * @Author: bilaisheng
     * @Wechat: 878799579
     * @Date: 2019/1/1 16:53
     * @Todo: 通过内部类实现Runnable,变量使用Volatile关键字修饰演示
     * @Version : JDK11 , IDEA2018
     */
    public class VolatileTest{
    
    	public static void main(String[] args) {
    		VolatileThread volatileThread = new VolatileThread();
    		new Thread(volatileThread).start();
    
    		while (true){
    			// if的判断volatile保证当时确实正确,然后线程a可能处于休眠状态,
    			// 线程b也判断不存在,b线程就new了一个。
    			// 然后a线程wake up,据需执行new volatile获取最新值。
    			if(volatileThread.isFlag()){
    				System.out.println("flag 此时为true !");
    				break;
    			}
    		}
    	}
    }
    
    class VolatileThread implements Runnable{
    
    	private volatile boolean flag = false;
    
    	@Override
    	public void run() {
    		try {
    			Thread.sleep(500);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		flag = true;
    
    		System.out.println(Thread.currentThread().getName() + " flag = " + flag);
    	}
    
    	public boolean isFlag() {
    		return flag;
    	}
    
    	public void setFlag(boolean flag) {
    		this.flag = flag;
    	}
    }
    
    

    运行结果如下图所示:
    在这里插入图片描述

    通过对比我们发现在通过Volatile修饰和不通过Volatile修饰的变量,输出结果竟然会有些偏差。到底是为什么呢?

    我们逐步拆解上面代码执行步骤:

    1、针对于不使用Volatile关键字修饰变量:

    • 步骤一:默认flag = false;
    • 步骤二main线程的缓存区域没有刷新 flag的值。所以flag 还是false。故没有输出<flag 此时为true !>
    • 步骤三:子线程输出 Thread-0 flag = true

    2、针对于使用Volatile关键字修饰变量:

    • 步骤一:默认flag = false;
    • 步骤二:主线程看到flag是被Volatile关键字修饰的变量。则获取最新的flag变量值,此时flag = true。故输出<flag 此时为true !>
    • 步骤三:子线程输出 Thread-0 flag = true

    1. Volatile的优点

    可见性:被Volatile修饰的变量可以马上刷新主内存中的值,保证其他线程在获取时可以获取最新值,所有线程看到该变量的值均相同。

    轻量级的synchronized,高并发下保证变量的可见性。


    6.Volatile的缺点

    1、频繁刷新主内存中变量,可能会造成性能瓶颈

    2、不具备操作的原子性,不适合在对该变量的写操作依赖于变量本身自己。例如i++,并不能通过volatile来保证原子性

  • 相关阅读:
    Mvvm combobox绑定Dictionary问题
    类型转化方法(处理System.Nullable类型)
    linq 动态查询
    VS 2005 / 2008 / 2010 能否继续使用 ASP.NET 1.x版的DataGrid ????
    使用 Using...End Using区块来写程序,要非常小心!
    [习题]TreeView、Menu、SiteMapPath #2 多国语系 /当地语系 / Localization
    Repeater,不用自己写循环 (Loop)
    [习题]给初学者的范例,多重字段搜寻引擎 for GridView,兼论 SqlDataSource与SelectParameter的用法
    ASP.NET案例精编(清华大学出版社 / 作者MIS2000Lab)「勘误表」、补充习题与档案下载
    Windows Vista / 7减少不必要的服务、最佳化(优化)
  • 原文地址:https://www.cnblogs.com/bilaisheng/p/10210896.html
Copyright © 2011-2022 走看看