JUC并发编程学习笔记(十六)Volatile

这篇具有很好参考价值的文章主要介绍了JUC并发编程学习笔记(十六)Volatile。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Volatile

保证可见性

private volatile static Integer num = 0;

使用了volatile关键字,即可保证它本身可被其他线程的工作内存感知,即变化时也会被同步变化。

不保证原子性

原子性:不可分割

线程A在执行任务时是不可被打扰的,也不能被分割,要么同时成功,要么同时失败。

package org.example.tvolatile;

public class VDemo02 {
    //synchronized保证原子性,每次只有一条线程执行,所以结果准确
    //volatile不保证原子性,虽然也是同步机制,但是结果不准确
    private volatile static int num = 0;
    public static void add(){
        num++;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2){//主线程和GC线程
            Thread.yield();//让主线程礼让
        }
        System.out.println(num);
    }

}

JUC并发编程学习笔记(十六)Volatile

每次结果也不一样。

如果不加Lock加synchronized,怎么保证原子性?

要了解为什么一个num++都不能保证原子性,首先我们需要查看到他编译好的class字节码文件,找到target,并且右键选择打开外部的文件,找到对应的class文件,再通过javap -c命令反编译查看字节码文件。

JUC并发编程学习笔记(十六)Volatile

JUC并发编程学习笔记(十六)Volatile

JUC并发编程学习笔记(十六)Volatile

JUC并发编程学习笔记(十六)Volatile

查看到字节码文件后可以看到从底层看,其实num++这个操作再多线程下并不安全,有获取和写回这两个操作

JUC并发编程学习笔记(十六)Volatile

那么又回到了这个问题,在volatile中如何保证原子性。打开jdk帮助文档,我们能找到原子性的一些数据类型

JUC并发编程学习笔记(十六)Volatile

JUC并发编程学习笔记(十六)Volatile

在volatile需要保证原子性操作的时候使用原子类来解决原子问题。

原子类为什么高级

原子类的包装类底层使用的是CAS操作。

package org.example.tvolatile;

import java.util.concurrent.atomic.AtomicInteger;

public class VDemo02 {
    //synchronized保证原子性,每次只有一条线程执行,所以结果准确
    //volatile不保证原子性,虽然也是同步机制,但是结果不准确
    private volatile static AtomicInteger num = new AtomicInteger(0);
    public static void add(){
        num.getAndIncrement();//AtomicInteger+1的方法,并不是简单的+1操作,方法:CAS
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2){//主线程和GC线程
            Thread.yield();//让主线程礼让
        }
        System.out.println(num);
    }

}

这些类的底层都直接调用c++和操作系统挂钩!在内存中修改值。Unsafe类是一个很特殊的存在!Unsafe类的所有方法都是native方法,调用c++底层

native是与C++联合开发的时候用的!java自己开发不用的!

禁止指令重排

什么是指令重排:你写的程序并不是按照你写的那样去执行的

源代码-----编译器优化的指令重排--------指令并行也可能会指令重排--------内存系统也会重排--------->执行

int x = 1;//1
int y = 2;//2
x = x+5;//3
y = x*x;//4

我们所期望的执行顺序:1、2、3、4;但是如果按照1、3、2、4或者2、4、1、3的顺序执行也是能够顺利运行的,计算机执行时可能对对我们所期望执行顺序的程序进行指令重排,结果是正确的但是对运行顺序有所不同

但是指令重排后的顺序不可能是4、3、2、1或者其它不符合逻辑的顺序。因为处理器在进行指令重拍的过程中会考虑数据之间的依赖性

可能造成影响的结果:四个初始值都为零

线程A 线程B
x = a; y = b;
b = 1; a = 2;

由于两个线程的操作都没有数据依赖性,指令重排就不会考虑顺序问题,可能会导致最终的执行顺序如下

线程A 线程B
b = 1; a = 2;
x = a; y = b;

多线程下可能导致一些问题

正常结果:x=0;y=0;

指令重排导致的诡异结果:x=2;y=1;

这是一种概念可能你写1000w行代码都不一定会出现,但是在理论上是一定会参数的。

volatile可以避免指令重排

计算机中存在着一种指令叫做内存屏障,它是一种CPU指令。

作用:

1、保证特定操作的执行顺序(可以让volatile避免指令重排)

2、可以保证某些变量的内存可见性(可以让volatile实现可见性)

JUC并发编程学习笔记(十六)Volatile

Volatile保证可见性、不能保证原子性、由于内存屏障,可以避免指令重排的现象产生!

内存屏障在单例模式中使用的最多!文章来源地址https://www.toymoban.com/news/detail-750693.html

到了这里,关于JUC并发编程学习笔记(十六)Volatile的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JUC并发编程学习笔记(八)读写锁

    ReadWriteLock ReadWriteLock只存在一个实现类那就是ReentrantReadWriteLock,他可以对锁实现更加细粒化的控制 读的时候可以有多个阅读器线程同时参与,写的时候只希望写入线程是独占的 Demo:

    2024年02月06日
    浏览(53)
  • JUC并发编程学习笔记(十九)原子引用

    带版本号的原子操作! 解决ABA问题,引入原子引用(乐观锁思想) AtomicStampedReference类解决ABA问题 所有相同类型的包装类对象之间值的比较全部使用equals方法比较 Integer使用了对象缓存机制,默认范围是-128至127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为v

    2024年02月05日
    浏览(56)
  • JUC并发编程学习笔记(四)8锁现象

    八锁-就是关于锁的八个问题 锁是什么,如何判断锁的是谁 对象、class模板 深刻理解锁 锁的东西无外乎就两样:1、同步方法的调用者,2、Class模板。 同一个锁中,只有当前线程资源释放后才会被下一个线程所接手。 同步方法的调用者是两个不同的实例时,互不相关。 静态

    2024年02月06日
    浏览(52)
  • JUC并发编程学习笔记(十)线程池(重点)

    线程池:三大方法、七大参数、四种拒绝策略 池化技术 程序的运行,本质:占用系统的资源!优化资源的使用!- 池化技术(线程池、连接池、对象池......);创建和销毁十分消耗资源 池化技术:事先准备好一些资源,有人要用就拿,拿完用完还给我。 线程池的好处: 1、

    2024年02月06日
    浏览(46)
  • JUC并发编程学习笔记(六)Callable(简单)

    callable接口和runnable接口类似,都是为了执行另外一条线程而设计的,区别是Runnable不会返回结果也不会抛出异常。 1、可以有返回值 2、可以抛出异常 3、方法不同;run()/call(); Runnable 实现Runnable接口,重写run方法,无返回值 Callable 实现Callable接口,重写call方法,有返回值,可

    2024年02月06日
    浏览(44)
  • JUC并发编程学习笔记(十八)深入理解CAS

    什么是CAS 为什么要学CAS:大厂你必须深入研究底层!有所突破! java层面的cas-------compareAndSet compareAndSet(int expectedValue, int newValue) 期望并更新,达到期望值就更新、否则就不更新! Unsafe类 java不能直接操作内存,但是可以调用c++,c++可以操作内存,java可以通过native定义

    2024年02月05日
    浏览(60)
  • JUC并发编程学习笔记(一)认知进程和线程

    进程 一个程序,如QQ.exe,是程序的集合 一个进程往往可以包含多个线程,至少包含一个 java默认有两个线程,GC垃圾回收线程和Main线程 线程:一个进程中的各个功能 java无法真正的开启线程,因为java是运行在虚拟机上的,所以只能通过C++,通过native本地方法调用C++开启线程

    2024年02月06日
    浏览(55)
  • JUC并发编程学习笔记(二)Lock锁(重点)

    传统的synchronized 传统的解决多线程并发导致的一些问题我们会使用synchronized来解决,synchronized的本质就是队列、锁。 Lock的实现类有:可重复锁(最常用)、读锁、写锁 在创建可重复锁时,可传入boolean类型值来决定该锁是公平锁(先来后到)还是非公平锁(可插队)

    2024年02月06日
    浏览(41)
  • JUC并发编程学习笔记(十二)Stream流式计算

    什么是Stream流式计算 大数据:存储+计算 集合、MySql这些的本质都是存储东西的; 计算都应该交给流来操作! 一个案例说明:函数式接口、lambda表达式、链式编程、Stream流式计算

    2024年02月05日
    浏览(48)
  • JUC并发编程学习笔记(七)常用的辅助类

    CountDownLatch 这是一个JUC计数器辅助类,计数器有加有减,这是减。 使用方法 使用前 可能会在所有人没出去之前关门 使用后 不在乎谁先出去,但是一定要总数等于0后才会关门 原理 countDownLatch.countDown();//总数减1 countDownLatch.await();//等待总数变为0才会往下执行,相当于阻塞当

    2024年02月06日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包