ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

volatile

2020-05-02 17:56:41  阅读:219  来源: 互联网

标签:Thread myData number 线程 内存 volatile


volatile是什么

volatile是java虚拟机提供的轻量级同步机制,它包含三种特性:

  • 保证可见性:只要主内存中变量做出修改,其余线程马上会感知到变量的修改。
package com.chinda.java.audition;


import java.util.concurrent.TimeUnit;

class MyData {
    volatile int number = 0;
    public void addTo60 (){
        this.number = 60;
    }
}

/**
 * 0. 先不添加volatile关键字, 执行是否会阻塞; 之后加上volatile关键字, 最后打印会不会执行。
 * 1.验证volatile可见性
 *  1.1 假如int number = 0; number变量没有添加volatile关键词修饰没有可见性
 *  1.2 添加volatile,可以解决可见性问题。
 *
 * @author Wang Chinda
 * @date 2020/5/2
 * @see
 * @since 1.0
 */
public class VolatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in!");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addTo60();
            System.out.println(Thread.currentThread().getName() + "\t update number value: " + myData.number);
        }, "T1").start();


        while (myData.number == 0) {
            // 主线程跳出此循环, 说明线程时可见, 若阻塞在此循环, 说明线程不可见。
        }
        System.out.println(Thread.currentThread().getName() + "\t mission is over! numer value: " + myData.number);
    }
}
  • 不保证原子性:
package com.chinda.java.audition;


import java.util.concurrent.atomic.AtomicInteger;

class MyData1 {
    volatile int number = 0;
    AtomicInteger atomic = new AtomicInteger();

    public void addPlus() {
        this.number++;
    }

    public void addAtomic() {
        atomic.getAndIncrement();
    }

}

/**
 * 1.验证volatile不保证原子性
 *  1.1 原子性: 即某个线程正在做某个具体业务, 中间不可以被加塞或者被分割。需要整体完整,要么同时成功,要么同时失败。
 *  1.2 一个线程将属性值写回主内存,没有来得及通知其他线程时, 有其他线程也要将属性值写回主内存, 属性值在主内存中出现覆盖现象。所以volatile不保证原子性。
 * 2. 解决非原子性问题。
 *  2.1 添加synchronized
 *  2.2 添加锁lock
 *  2.3 用原子类AtomicInteger
 *
 * @author Wang Chinda
 * @date 2020/5/2
 * @see
 * @since 1.0
 */
public class VolatileDemo1 {
    public static void main(String[] args) {
        MyData1 myData = new MyData1();

        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addPlus();
                    myData.addAtomic();
                }
            }, String.valueOf(i)).start();
        }

        // 等待上面20个线程全部计算完成后, 再用main线程取得最终的计算结果。
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t finally! numer value: " + myData.number);
        System.out.println(Thread.currentThread().getName() + "\t finally! atomic value: " + myData.atomic.get());
    }
}
  • 禁止指令重排

JMM概念

​ JMM本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(实例字段、静态字段、构成数组对象的元素)的访问方式。包含三种特性:

  • 可见性
  • 原子性
  • 有序性

JMM同步规定

  1. 线程解锁前,必须把共享变量的值刷回主内存。

  2. 线程加锁前,必须读取主内存内最新值到线程的工作内存中。

  3. 加锁与解锁是同一把锁。

    由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存,工作内存时内阁线程私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存时共享内存区域,所有线程都可以访问,但线程对变量的操作必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成将变量写回主内存。

volatile修饰JMM

noneVolatile
volatile修饰时,主内存中属性修改,各线程中时不可见的。各个线程中工作内存只以运算前在主内存中拷贝的属性值为准。

volatile修饰JMM

volatile
volatile修饰时,主内存中的属性修改,各个线程中时可见的,其中一个线程将工作内存中的属性写回主内存中会通知各个线程去同步主内存中的属性值。但是若在通知之前已经将工作内存中的属性修改,再收到通知同步,这时候就会出现属性值覆盖情况。例如上图中,你品,你细品。即volatile可保证线程间的可见性,但是不保证线程中数据的原子性。

标签:Thread,myData,number,线程,内存,volatile
来源: https://www.cnblogs.com/chinda/p/12818825.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有