CAS+volatile+两个小工具(有误待更)

内存模型

JMM模型
在这里插入图片描述

JMM实际上就是CPU特有的一级二级缓存与共享内存之间的关系。
每个cpu有自己的缓存,上级缓存从下级缓存中选取数据,所有缓存都是通过三级缓存和内存做交换,而三级缓存是所有cpu共有的。

重排序

重排序场景

1 编译器重排,为了避免重复计算或读写,完成一些重排,并给指令添加内存屏障,防止指令重排时出现不符合语义的重排
2 指令重排,为了保证ILP流水线的并行,重排指令。和编译器重排的区别在于,上一级把整条指令作为是否重排的判断,而这一级把指令拆分成若干阶段,以指令的阶段作为是否重排的判断标准。
3 读写缓存,缓存和实际内存必然导致一致性问题产生一种和重排相似的效果

重排序问题避免

以下两种协议是JVM用于限制指令重排,防止出错的。
as if serial 是一些用来保证单线程顺利执行的规则
happens-before是一些用来保证单线程顺利执行的规则,如果调换之后的预期结果不变,happens-before可以不被遵守

volatile

语义

volatile写之前所有写对后续读可见,volatile读的是数据最新版本

原理

简介

要做到保证volatile语义,就需要保证两件事,语句重排不会影响volatile语义和各cpu并行执行volatile命令时不会相互重叠

语句重排通过屏障保护语义

volatile写之前通过storestore屏障,这是因为volatile写之后所有缓存会写回内存,为了保证此语义,所有之前写操作不能被延后
volatile写之后通过storeload屏障,保证一切读指令不能到volatile写之前,保证了读写的可见性

volatile读之前不需要屏障,因为volatile读之后storeload屏障保证了读指令不可能在volatile写之前
volatile读之后需要loadstore和loadload屏障,这个能起到的作用是防止指令后移,只能前移(并且不可以前移到volatile读/写前)
在这里插入图片描述
在这里插入图片描述
实际上编译时可以省下不少屏障
在这里插入图片描述

cpu并行处理命令时

volatile写通过总线锁或缓存锁保证同步
总线锁可以直接锁总线,直接改内存,废弃其它缓存。
而缓存锁只锁自己对应的缓存行,并不阻止其他cpu缓存写回内存,所以需要配合缓存一致性协议来避免写回冲突。

MESI

一种常见的缓存一致性协议,也是volatile所采用的
M:Modified,自己修改了,没来得及写回内存
E:Exclusive,自己修改了,还写回内存了
S:Shared,自己保存着和其他缓存,主内存相同的数据
I:Invalid,自己保存的缓存数据已经无效了
一开始全部cpu共享内存中一个数据,属于s状态,如果有一个cpu提出volatile写,就会变成m,如果是多个cpu同时提出volatile写,就会引起总线裁决,只有一个会变成m状态,其余就会变成i状态。
比如在atomic包中,原子操作Integer完成一次累加,往往要先取值完成一次volatile读,然后再提出累加,如果失败,就会重复这个过程。
这是因为volatile写竞争失败的cpu想要继续完成写操作,必须保证自己不是i状态,要重新读取内存数据,更改自己状态为s,然后才可以继续发出写信号。
如果volatile写竞争成功,那么自己会变成m状态,如果写回成功,那自己会变为e状态。在m状态变成e状态之后,其他cpu才可以正常读取内存数据。
如果在volatile读时发现自己是i状态,就会去获取内存数据获取新的缓存。

缓存行锁定时的小trick

由于一行往往是64字节,所以有时候对于并发读写非常频繁的地方,可以将缓存行填充到64字节。
如图,由于queue对首尾节点访问非常频繁,所以把首尾节点都填充到64字节。
在这里插入图片描述

volatile实战

在这里插入图片描述

如图,内存分配实际上有三步组成,分配空间,初始化对象,引用指向,而后才允许其他线程读取,如果顺序变为分配空间,引用指向,然后其它线程读取,发现部位null,就会返回空对象。

坏处

频繁使用volatile会导致总线上很多缓存行状态更改信息,而占用了真正传递信息的资源。
而如果在高并发时候,由于CAS的存在,这个现象会更加明显。

CAS

unsafe

unsafe是一个危险的类,因为它通过地址直接对数据进行操作,里面是一些完成get,set和CAS的本地方法,一般建议使用atomic包下的类,它们对unsafe进行了一层封装

使用

分为数值原子操作,引用原子操作,数组原子操作,和属性原子操作
操作简单,没什么好说的

并发工具类

CyclicBarrier

CyclicBarrier初始化时会放入等待数和Runnable。
如果线程到达了await(),就会减少等待数,如果没到0,就会阻塞,如果到0了,就会执行Runnable,然后放行所有之前阻塞线程。

public class CyclicBarrierTest implements Runnable{
    /**
     * 使用 CyclicBarrier
     */
    CyclicBarrier cb = new CyclicBarrier(2,this);
    int[] array = new int[2];
    public void calcute(){
        Thread A = new Thread(() -> {
            //计算 3*5
            array[0] = 3*5;
            try {
                cb.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        });
        Thread B = new Thread(() -> {
            //计算 10+2
            array[1] = 10+2;
            try {
                cb.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        });
        A.start();
        B.start();
    }

    @Override
    public void run() {
        System.out.println(array[0] + array[1]);
    }

    public static void main(String[] args) {
        CyclicBarrierTest2 cyclicBarrierTest2 = new CyclicBarrierTest2();
        cyclicBarrierTest2.calcute();
    }

    // 我们的cycbarrier 能够支持一个runnable的action去做后续的数据的操作。能够适用于更加复杂的
    // 场景。
}

Semaphore

用来限制资源数,如下,100个线程只有10个可以获取资源继续访问

public class SemaphoreTest {
    static Semaphore s = new Semaphore(10);
    public static void main(String[] args) {
        for(int i = 0 ; i < 100; i++) {
            Thread a = new Thread(() -> {
                try {
                    s.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("链接db,保存数据。");
                s.release();
            });
            a.start();
        }
    }
}

声明

参考了河北王校长的视频
来源是本人语雀笔记(语雀分享竟然收费了!失踪人口回归)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值