栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Volatile

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Volatile

以下都是个人理解,有大佬看到不对的请指教

在了解Volatile必须先JVM模型和JMM

JMM:

JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式.

JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的.

从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:

线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。

JMM规范:

JMM规定了所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory),线程的工作内存中保存了该线程使用到的变量的主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序性规定,所以看起来如同直接在主内存中读写访问一般)。不同的线程之间也无法直接访问对方工作内存中的变量,线程之间值的传递都需要通过主内存来完成

JMM同步:

        线程加锁前,必须读取主内存的最新值到自己的工作内存
        线程解锁前,必须把共享变量的值刷新回主内存
        加锁解锁是同一把锁

简单来说,线程操作对象都是从:

        主内存 --->  拷贝 ---> 自己的工作内存 ---> 操作后 ---> 写回主内存    

 数据的读取传递:硬盘 < 内存  < cache(缓存) < CPU(计算)

JMM的三个特征

可见性:当某个线程修改值后(线程工作内存中存储的是主内存中变量的副本),写回主内存后,其他线程立马知道。一句话:多线程中,一个线程对共享变量做了修改之后,其他的线程立即能够感知到该变量的这种修改。

原子性:一个操作不能被打断,要么全部执行完毕,要么不执行

有序性:计算机执行程序时,为了提高性能,编译器 和 处理器 经常会进行:指令重排

                分三种:源码--->编译器优化重排--->指令并行重排--->内存系统重排--->最终执行处理器

                   在重排时,必须考虑指令之间的:数据依赖 ,控制依赖性

 volatile:

        是java虚拟机提供的轻量级同步机制

两大特性:

保证可见性:

当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果
当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。
当一个线程用到被volatile关键字修饰的值的时候,虚拟机会强制要求它从主内存中读取
public class Test  {
	    public volatile int num;
	    public void operate () {
	        this.num = 3;
	    }
	    //测试
	    public static void main(String[] args) {
	        Test test = new Test();
	        new Thread(() -> {
	            System.out.println(Thread.currentThread().getName()+" ---》");
	            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
	            //修改
	            test.operate();
	        },"testOperate").start();

	        while (test.num == 0) {
	            System.out.println("--------------等待修改----------------------------");
	        }
	        System.out.println("修改了");
	    }
	}

禁止指令重排

idea可以自己添加:external tool 查看

它是不能保证原子性的

	public class Test  {

	    public volatile int num;

	    public void operate () {
	        num++;
	    }
	    //测试
	    public static void main(String[] args) {
	        Test test = new Test();
	        //计算10*2000
	        for (int i = 0; i < 10; i++) {
	            new Thread(() -> {
	                for (int j = 0; j < 2000; j++) {
	                    new Thread(() -> test.operate()).start();
	                }
	            }).start();
	        }

	        //等待上面线程计算完成
	        while (Thread.activeCount() > 1) {
	            Thread.yield();
	        }
	        System.out.println("正确结果:" + 10 * 2000);
	        System.out.println("实际结果:"+test.num);
	    }
	}		
	

解决不保证原子性的方式:

加:synchronized,lock... 原子类

public class Test  {
		//    public volatile int num;
		    public volatile AtomicInteger  num= new AtomicInteger();
		    public  void operate () {
		//        num++;
		        num.getAndIncrement();
		    }
		    //测试
		    public static void main(String[] args) {
		        Test test = new Test();
		        //计算10*2000
		        for (int i = 0; i < 10; i++) {
		            new Thread(() -> {
		                for (int j = 0; j < 2000; j++) {
		                    new Thread(() -> test.operate()).start();
		                }
		            }).start();
		        }

		        //等待上面线程计算完成
		        while (Thread.activeCount() > 1) {
		            Thread.yield();
		        }
		        System.out.println("正确结果:" + 10 * 2000);
		        System.out.println("实际结果:"+test.num.get());
		    }
		}
DCL(双端检锁):

DCL会出现出现: 线程1分配了空间,发生指令重排,先建立了引用,随后cpu切换到了进程2 ,这个时候判断实例不为空拿到的半实例化的对象所出现的问题

有指令重排存在,test!=null的时候,test实列可能没有实例化完成,双重锁需要volatile修饰对象的,不然有安全问题    

public class Test  {
    private static volatile Test test = null;
    private Test(){
        System.out.println(Thread.currentThread().getName()+"---------------------------");
    }
    public static Test getTest(){
        if(null == test){
            synchronized(Test.class) {
                if(null == test){
                    test = new Test();
                }
            }
        }
        return test;
    }
    //测试
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> Test.getTest()).start();
        }

    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/462462.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号