线程的可见性、有序性、一致性

volatile

一个小程序

https://gitee.com/zxj8524210/my-thread-test/blob/master/src/main/java/org/example/App1.java

在这里插入图片描述

说明:
每一个线程从内存中读取一个变量,读到的都是一个副本。在当前线程修改变量的值,只会影响到当前线程从内存中读取到的副本的值,并不会影响到内存中本身的变量值。线程之间变量修改是不可见的。如果想要线程之间可见,需要给该变量加上volatile关键字,或者使用 synchronized关键字触发 内存刷新机制。

注意:
如果使用 volatile 去修饰一个对象。该对象内部的变量依然是不可见的。

三层缓存

程序从内存中读取数据,每次会读取一行数据,这个数据行的大小是64字节。例如,有两个线程读取不通的数据,读取到的数据又存在公共的部分,因此在两个线程的缓存之间会相互影响,最后导致性能没有充分的释放。 因此可以在需要读取的数据加上 @Contended 注解 并且 在JVM的启动参数上添加-XX:-RestrictContended,来确保多个线程读取到的变量不在同一个缓存行上。

注意:
volatile和缓存行的概念没有半毛钱关系

线程的乱序执行

触发条件:

  • 只要不影响线程的最终一致性。

有序性代码实验:
https://gitee.com/zxj8524210/my-thread-test/blob/master/src/main/java/org/example/App3.java

有序性 和 可见性 实验代码:
https://gitee.com/zxj8524210/my-thread-test/blob/master/src/main/java/org/example/App4.java

汇编指令乱序问题:
比如在main方法中new了一个对象 ,它的会变指令通过jclasslib插件,可以观察到这行代码最后被翻译成5条汇编指令

public class T {
    private int m=8;

    public T() {
    }

    public int getM() {
        return m;
    }

    public void setM(int m) {
        this.m = m;
    }
}
    public static void main(String[] args) {
        T t= new T();
    }

最终获得到汇编指令:
在这里插入图片描述

  • new 指令 意思是申请内存
  • invokespecial 指令为调用类的构造方法。在调用该指令之前,对象还未完全初始化,对象中m变量还只是默认值,调用该指令之后,m的值才是8
  • astore_1为把内存中的对象和栈中的t做关联

一个存在问题的实验小程序:

https://gitee.com/zxj8524210/my-thread-test/blob/master/src/main/java/org/example/App7.java

原子性

原子性问题小程序:
https://gitee.com/zxj8524210/my-thread-test/blob/master/src/main/java/org/example/App8.java

上锁的本质就是把 并发编程序列化。
实验代码:
https://gitee.com/zxj8524210/my-thread-test/blob/master/src/main/java/org/example/App9.java

volatile 和 synchronized的区别

volatile保障了 有序性和可见性,不保障原子性。
synchronized 保障了 原子性和可见性,不保障有序性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值