并发编程-volatile使用

本文详细介绍了Java中的volatile关键字,包括其确保多线程下变量可见性、禁止指令重排序以及在并发编程中的应用。讲解了volatile的内存语义、特性、使用场景以及与synchronized的区别,强调了volatile在特定条件下的线程安全性。
摘要由CSDN通过智能技术生成

第一章 volatile关键字概览

1.1 多线程下变量的不可见性

1.1.1 概述

在多线程并发执行下,多个线程修改共享的成员变量,会出现一个线程修改了共享变量的值后,另一个线程不能直接看到该线程修改后的变量的最新值

1.1.2 案例演示

/*
    目标:研究一下多线程下变量访问的不可见性现象

    准备内容:
        1.准备2个线程
        2.定义一个成员变量
        3.开启两个线程,其中一个线程负责修改,一个线程负责读取
 */
public class VisibilityDemo01 {
   
    // main方法,作为一个主线程
    public static void main(String[] args) throws InterruptedException {
   
        myThread myt = new myThread();
        Thread t = new Thread(myt);
        t.start();

        while(true){
   
            if(myt.isFlag()) {
   
                System.out.println("主线程");
            }
        }
    }
}

class myThread implements Runnable{
   
    //成员变量
    private boolean flag = false;

    public boolean isFlag() {
   
        return flag;
    }

    public void setFlag(boolean flag) {
   
        this.flag = flag;
    }

    @Override
    public void run() {
   
        try {
   
            Thread.sleep(100);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        flag = true;
        System.out.println(true);
    }
}

19.多线程中卖电影票、送牛奶均为案例演示

1.1.5 结果展示

在这里插入图片描述

1.1.4 小结

多线程下修改共享变量会出现变量修改值后的不可见性

1.2 变量不可见性内存语义

1.2.1 概述

1.3 变量不可见性解决方案

1.3.2 结局方案

加锁
某一个线程进入synchronized代码块前后,执行过程如下:
a.线程获得锁
b.清空工作内存
c.从主内存拷贝共享变量最新的值到工作内存成为副本
d.执行代码
e.将修改后的副本的值刷新回主内存中
f.线程释放锁

volatile关键字修饰
a.子线程从主内存读取到数据放入其对应的工作内存
b.将flag的值更改为true,但是这个时候flag的值还没有写回主内存
c.此时main方法读取到了flag的值为flase
d.当子线程将flag的值写回去后,其他所有线程对此变量的副本都失效
e.再次对flag进行操作的时候,操作的线程就会从主内存读取最新的值,放入到工作内存中

总结 volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值

第二章 volatile的其他特性

2.1 volatile特性概述

volatile总体概览
在上一章中,我们已经研究完了volatile可以实现并发下共享变量的可见性,除了volatile可以保证可见性外,volatile还具备如下一些突出的特性:
volatile的原子性问题:volatile不能保证原子性操作。
禁止指令重排序:volatile可以防止指令重排序操作。

2.2 volatile不保证原子性

所谓原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行。volatile不保证原子性

2.2.1代码测试:

public class VolatileDemo {
   
    public static void main(String[] args) {
   
        VolatileAtomicThread vat = new VolatileAtomicThread();

        for(int x=0;x<100;x++){
   
            new Thread(vat).start();
        }
        System.out.println(vat.getI());
    }
}

class VolatileAtomicThread implements Runnable{
   
    private volatile int i=0;

    public int getI() {
   
        return i;
    }

    @Override
    public void run() {
   
        for(int j=0;j<10000;j++){
   
            i++;
            System.out.println("count" + i);
        }
    }
}

2.2.2 小结

在多线程环境下,volatile关键字可以保证共享数据的可见性,但是并不能保证对数据操作的原子性(在多线程环境下volatile修饰的变量也是线程不安全的)。

在多线程环境下,要保证数据的安全性,我们还需要使用锁机制

经自己尝试,synchronized、lock都能保证原子性

2.2.3 问题解决

使用锁机制
synchronized、lock
原子类
概述:java从JDK1.5开始提供了java.util.concurrent。atomic包(简称Atomic包),这个包中的原子操作类提供一种用法简单,性能高效,线程安全地更新一个变量地方式。

AromicInteger
原子型Integer,可以实现原子更新操作

class VolatileAtomicThread01 implements Runnable{
   
    private AtomicInteger ai = new AtomicInteger(); //初始值默认为0
    @Override
    public void run() {
   
        for(int j=0;j<10000;j++){
   
            System.out.println(Thread.currentThread().getName() + "count" + ai.incrementAndGet());
        }
    }
}

2.3 禁止指令重排序

2.3.1 概述

什么是重排序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值