JUC_volatile使用

一、前提(JMM)

1、JMM概念

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

2、JMM同步的规定

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

3、补充说明

由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储这主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程之间的通信(传值)必须通过主内存来完成

二、概述

1、说明

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

2、特性:

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排

三、代码验证

3.1代码验证可见性

3.1.1未加volatile处理

package com.atguigu.juc;

import java.util.concurrent.TimeUnit;

class MyData {
    int number = 0;

    public void addT060() {
        this.number = 60;
    }
}

/**
 * 1.验证volatile的可见性
 * 1.1 假如 int number = 0; number变量之前一直没有用volatile关键字修饰
 */
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);
                myData.addT060();
                System.out.println(Thread.currentThread().getName() + "\t update number value:" + myData.number);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "AAA").start();

        //
        while (myData.number == 0) {
            //main线程一致在这里等待循环,直到number值不等于0
        }
        System.out.println(Thread.currentThread().getName() + "\t mission is over , main get number value:" + myData.number);
    }
}

 3.1.2添加volatile处理

package com.atguigu.juc;

import java.util.concurrent.TimeUnit;

class MyData {
    volatile int number = 0;

    public void addT060() {
        this.number = 60;
    }
}

/**
 * 1.验证volatile的可见性
 * 1.1 假如 int number = 0; number变量之前一直没有用volatile关键字修饰
 */
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);
                myData.addT060();
                System.out.println(Thread.currentThread().getName() + "\t update number value:" + myData.number);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "AAA").start();

        //
        while (myData.number == 0) {
            //main线程一致在这里等待循环,直到number值不等于0
        }
        System.out.println(Thread.currentThread().getName() + "\t mission is over , main get number value:" + myData.number);
    }
}

 3.2代码验证不保证原子性

package com.atguigu.juc;

import java.util.concurrent.TimeUnit;

class MyData {
    volatile int number = 0;

    public void addT060() {
        this.number = 60;
    }
    //请注意此时number 是用volatile进行修饰的
    public void addPlusPlus() {
        number++;
    }
}

/**
 * 1.验证volatile的可见性
 *      1.1 假如 int number = 0; number变量之前一直没有用volatile关键字
 *      1.2 添加了volatile可以解决可见性问题
 * 2.验证volatile不保证原子性
 *      2.1原子性指的是什么意思?
 *          不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者被分割
 *          需要整体完整,要么同时成功,要么同时失败
 *      2.2volatile不保证原子性演示
 */
public class VolatileDemo {
    public static void main(String[] args) {//一切方法的运行入口
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(() ->{
                for (int j = 1; j <= 1000; j++) {
                    myData.addPlusPlus();
                }
            }, String.valueOf(i)).start();
        }
        //需要等待上面20个线程全部计算完成后,再用main线程去的最终的结果值是多少?
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\t finally number value:"+myData.number);
    }

    //volatile可以保证可见性,及时通知其他线程,主物理内存的值已经被修改
    public static void seeOkVolatile() {
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
                myData.addT060();
                System.out.println(Thread.currentThread().getName() + "\t update number value:" + myData.number);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "AAA").start();

        //
        while (myData.number == 0) {
            //main线程一致在这里等待循环,直到number值不等于0
        }
        System.out.println(Thread.currentThread().getName() + "\t mission is over , main get number value:" + myData.number);
    }
}

如何解决

package com.atguigu.juc;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyData {
    volatile int number = 0;

    public void addT060() {
        this.number = 60;
    }
    //请注意此时number 是用volatile进行修饰的
    public void addPlusPlus() {
        //1.获得;2.增加;3:写回
        number++;
    }

    AtomicInteger atomicInteger = new AtomicInteger();
    public void addMyAtumic(){
        atomicInteger.getAndIncrement();
    }
}

/**
 * 1.验证volatile的可见性
 *      1.1 假如 int number = 0; number变量之前一直没有用volatile关键字
 *      1.2 添加了volatile可以解决可见性问题
 * 2.验证volatile不保证原子性
 *      2.1原子性指的是什么意思?
 *          不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者被分割
 *          需要整体完整,要么同时成功,要么同时失败
 *      2.2volatile不保证原子性演示
 *
 *      2.3为什么?
 *
 *      2.4如何解决原子性?
 *          *加 sync
 *          *
 */
public class VolatileDemo {
    public static void main(String[] args) {//一切方法的运行入口
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(() ->{
                for (int j = 1; j <= 1000; j++) {
                    myData.addPlusPlus();
                    myData.addMyAtumic();
                }
            }, String.valueOf(i)).start();
        }
        //需要等待上面20个线程全部计算完成后,再用main线程去的最终的结果值是多少?
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\t int type, finally number value:"+myData.number);
        System.out.println(Thread.currentThread().getName()+"\t AtomicInteger type, finally number value:"+myData.atomicInteger);
    }

    //volatile可以保证可见性,及时通知其他线程,主物理内存的值已经被修改
    public static void seeOkVolatile() {
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
                myData.addT060();
                System.out.println(Thread.currentThread().getName() + "\t update number value:" + myData.number);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "AAA").start();

        //
        while (myData.number == 0) {
            //main线程一致在这里等待循环,直到number值不等于0
        }
        System.out.println(Thread.currentThread().getName() + "\t mission is over , main get number value:" + myData.number);
    }
}

 3.3指令重排

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值