并发编程之JMM模型&Volatile底层原理

Python爬虫实战:逆向解析天猫商品详情API,精准捕获规格、SKU与促销活动 及浏览器开发者工具),绕过前端渲染的复杂性,直接逆向分析并调用其内部数据接口,从而高效、稳定地抓取商品的关键信息,包括商品规格、多维度SKU信息以及复杂的促销活动。print(f"SKU ID: {sku.sku_id}, 价格: ¥{sku.price}, 库存: {sku.stock}, 规格: {sku.props}")当你访问一个商品链接时,浏览器首先会加载一个几乎为空的HTML骨架,然后通过执行大量的JavaScript代码,向后台服务器发起多个异步请求,最终将获取到的数据填充到页面中。 阅读详情

并发编程的本质

并发编程为什么要有?为什么我们要做并发编程,并发编程是实质是什么?我觉得并发编程是一种艺术,在现在的时代,硬件的性能在不断的提升,以前的单核CPU现在已经发展成为了多核CPU,性能已经不断提升,如果我们还保留着以前的单线程程序的话,那么只会出现硬件性能过剩的情况;我们来思考一个问题并发编程的本质是解决什么问题?并发编程在一定的意义上其实就是多线程编程,那么多线程编程中主要有哪些,多线程编程中会涉及到同步操作,线程互斥操作,大的任务进行拆解,并发处理;所以并发编程的实质就是为了解决我们的业务需求,从而尽可能的压榨CPU的性能,达到性能的最大化;可以说在现在的大多数应用程序中,多线程的身影无处不在,单线程的应用已经成为过去时,如果非要说多线程的应用场景是什么,我可以说多线程的应用场景无处不在,适用于任何的场景下。

JMM模型

JMM模型就是JAVA的内存模型,但是这里我更喜欢叫JAVA的线程内存模型,我们都知道我们的JVM的内存模型中有堆,元空间还有虚拟机栈,虚拟机栈由执行引擎执行时会在虚拟机栈中创建栈桢来保存我们的变量信息和操作信息,所以JMM模型和虚拟机就有很大的关系,当我们创建一个对象时,比如new User()和 new Thread()这两者有什么区别呢?相同点是都时在堆区创建一个java对象,而我们都知道Thread是java的线程对象,通过它可以启动一个线程去执行我们的任务,而User只是一个普通的java对象,那么他们两者有什么区别呢?当我们创建一个线程Thread,其实不是JAVA开辟了一个线程,Thread底层启动线程和线程的操作是调用的native本地方法的,所以其实创建线程是交给了内核去做的,JVM本身是不具备调度CPU的权限的 ,是由内核去调度CPU的,比如看下图:
在这里插入图片描述
说白了就是JVM不具备调用CPU的权限,是交给了操作系统去执行的,实现从用户态向内核态的转变。我们都知道JAVA应用程序可以在windows、linux、unix等操作系统上运行,但是我们编写的多线程程序只有一份,也没有在程序中区分操作系统而执行不同的代码块,所以java应用程序创建线程交给操作系统内核的时候这个时候是屏蔽了不同操作系统的差异的,我们可以把它抽象出来,抽象出来就是我们不管操作系统的类型,我写的这个多线程在不同的操作系统上都能执行,并且都能执行成功而达到预期。

并发编程带来的风险

并发编程是好,并发编程会带来性能的提升,但是它就真的好,无脑使用吗?不是的,并发编程还是要根据实际的业务场景和熟练的程度来使用,否则就会带来一定的风险,那么会带来哪些风险?1.性能的问题:就是说我们的业务场景是否需要启动多线程来执行我们的这些任务,这个要根据数据量和处理的任务复杂度来考虑,比如有个需求可以用单线程执行的,你非要使用多线程来执行,那么性能不一定会提升,反而会降低系统性能,为什么呢?因为多线程是要考虑到线程间的上下文切换带来的性能损耗的;是不是系统出现了一些问题,会有人经常给你说,这段程序操作的原因是因为多线程的上下文切换带来的性能开销导致了一些问题;而单线程是一直占用cpu的执行权限的,它可以一直执行,直到执行完成,所有是否采用多线程还是要根据要处理的任务的复杂度以及数据量的大小,多线程在这种情况下就不一定会达到性能提升的效果,我们来看下多线程的上下文切换的图:
在这里插入图片描述
所以要根据实际的情况考虑是否采用多线程,多线程是好,可以尽可能的压榨CPU的性能,但是多线程会有线程的上下文切换,所以这个要考虑到性能的开销来觉得是否采用多线程。2.线程的活跃性问题:线程的活跃性问题有饥饿,死锁和活锁 饥饿:就是在多线程的环境下,比如有些线程被调了线程的优先级非常低,那么有可能这个线程永远都不会被调度,永远处于饥饿的状态,类似于死锁一样,永远不会被调度;所以这种情况下,要特别注意这种情况的发生,也就是说线程的优先级是可以影响到线程获取CPU执行周期,一个线程启动了永远得不到CPU的执行时间周期,那么就会被活活饿死。死锁:死锁这个比较好理解,就是Thrad1锁了a对象,依赖b对象,而Thrad2锁了b对象,依赖a对象,那么就会出现相互依赖,永远无法释放对象锁,就会出现死锁

public class T0915 {

    final static Object a = new Object();
    final static Object b = new Object();

    public static void main(String[] args) throws InterruptedException {
       new Thread(()->{
           System.out.println(Thread.currentThread().getName()+"开始执行...");
           synchronized (a){
               System.out.println(Thread.currentThread().getName()+" 获得a对象执行...");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               synchronized (b){
                System.out.println(Thread.currentThread().getName()+" 依赖b对象执行...");

            }
           }
       },"Thread1").start();


        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"开始执行...");
            synchronized (b){
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" 获得b对象执行...");
                synchronized (a){
                    System.out.println(Thread.currentThread().getName()+" 依赖a对象执行...");

                }
            }
        },"Thread2").start();
    }
}

活锁:活锁是什么意思呢?活锁就是说你获取了锁没有意义,就是说你获取了锁没有干正事儿,比如两个线程,第一个线程获取了锁,发现需要让步给其他锁,其他锁获取了锁,也让给了其他锁,这样一来一往,其实大家都灭有干正事儿就是活锁


public class LiveLockTest {

    /**
     * 定义一个勺子,ower 表示这个勺子的拥有者
     */
    static class Spoon {
        Diner owner;

        public Spoon(Diner diner) {
            this.owner = diner;
        }

        public String getOwnerName() {
            return owner.getName();
        }

        public void setOwner(Diner diner) {
            this.owner = diner;
        }

        //表示正在用餐
        public void use() {
            System.out.println(owner.getName() + " 用这个勺子吃饭.");
        }
    }

    /**
     * 定义一个晚餐类
     */
    static class Diner {

        private boolean isHungry;
        //用餐者的名字
        private String name;

        public Diner(boolean isHungry, String name) {
            this.isHungry = isHungry;
            this.name = name;
        }

        //和某人吃饭
        public void eatWith(Diner diner, Spoon sharedSpoon) {
            try {
                synchronized (sharedSpoon) {
                    while (isHungry) {
                        //当前用餐者和勺子拥有者不是同一个人,则进行等待
                        while (!sharedSpoon.getOwnerName().equals(name)) {
                            sharedSpoon.wait();
                        }
                        if (diner.isHungry()) {
                            System.out.println(diner.getName()
                                    + " 饿了," + name + "把勺子给他.");
                            sharedSpoon.setOwner(diner);
                            sharedSpoon.notifyAll();
                        } else {
                            //用餐
                            sharedSpoon.use();
                            sharedSpoon.setOwner(diner);
                            isHungry = false;
                        }
                        Thread.sleep(500);
                    }
                }
            } catch (InterruptedException e) {
                System.out.println(name + " is interrupted.");
            }
        }

        public boolean isHungry() {
            return isHungry;
        }

        public void setHungry(boolean hungry) {
            isHungry = hungry;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public static void main(String[] args) {
        final Diner ant = new Diner(true, "ant");
        final Diner monkey = new Diner(true, "monkey");
        final Spoon sharedSpoon = new Spoon(monkey);

        Thread h = new Thread(()->ant.eatWith(monkey, sharedSpoon));
        h.start();


        Thread w = new Thread(()->monkey.eatWith(ant, sharedSpoon));
        w.start();

//        try {
//            Thread.sleep(10000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        //   h.interrupt();
        //   w.interrupt();

    }
}

3.线程的数据安全问题:线程数据安全问题就是比较常见而且也是多线程编程的一个老话题了,针对线程安全问题,一般通过锁来控制线程的数据安全,锁机制在java多线程开发过程中大体分为两种,内置锁和JUC锁:内置锁:使用Synchornized,synchronized是java的关键字,如果简简单单的通过看底层源码的方式是没有办法看到的,需要通过汇编指令来看才能理解这个锁的机制,synchronized锁的对象,不是代码块,这个概念一定要弄清楚,因为经常会听到说锁代码块,锁方法,其实它是锁的对象;synchronized锁是不需要程序员释放的,它本身会自己释放锁,底层有一个锁升级的过程,依次为自适应锁(自旋锁)、偏向锁、轻量级锁、重量级锁,synchronized是基于object monitor的机制来实现的,默认是非公平锁。JUC锁:JUC下面的锁比较常用的有ReentrantLock和LockSupport,这个锁是JDK的已有的实现,JUC锁是独占锁、共享锁、重量级锁、公平锁、非公平锁以及读写锁;JUC锁都有一个特征如果你手动加锁了,记得要手动释放,jvm是不会给你释放锁的,一般我们会在finally中去释放锁,保证整个锁能够释放。

计算机组成

在这里插入图片描述
计算机的组成如上图,其中CPU是计算机的大脑部分,主要负责运算,CPU有算数逻辑单元、寄存器、PC、还有高速缓存,现在的CPU都有三级缓存,每一级缓存的容量都是递增的,其中第三极缓存是CPU中共享的;CPU通过系统总线将数据从主内存中加载到缓存中,然后进入寄存器,最后进入ALU进行运算,最后在通过总线回写到主内存中,如:
在这里插入图片描述
CPU中的L1、L2是和其他CPU不共享的,其中L3是每个CPU共享的,当需要CPU需要计算时,从主内存加载到L3,然后再一步一步加载到寄存器中,最后完在ALU中完成运算,最后再回写到主存中;
我们了解了计算机的大体组成过后,我们来思考一个问题多线程编程中并发有哪些特性呢?并发特性:通俗点说就是保证原子性、有序性、可见性,那么如何保证呢?下面我们通过例子来分析

并发之可见性

public class T0916 {

    private static boolean falg = true;


    public static void main(String[] args) throws InterruptedException {
        T0916 t0916 = new T0916();
        new Thread(() -> t0916.thread1(),"thread1").start();
        Thread.sleep(2000);
        new Thread(() -> t0916.thread2(),"thread2").start();

    }


    private void thread1(){
        System.out.println(Thread.currentThread().getName()+"开始启动...");
        int i = 1;
        while (falg){
            i++;
        }
        System.out.println(Thread.currentThread().getName()+" 结束 i="+i);
    }

    private void thread2(){
        System.out.println(Thread.currentThread().getName()+"开始启动...");
        falg = false;
        System.out.println(Thread.currentThread().getName()+"执行结束...");
    }
}

这个例子的大体意思就是定义了一个全局变量falg=true,我启动一个线程,执行while,然后2s过后,我启动另一个线程,修改了falg的值,看下线程1是否能够退出?执行截图如下:
在这里插入图片描述
从图中可以看出,线程一直没有退出,为什么呢?明明我的线程2已经修改了值了,为什么不退出呢?我们修改下程序,我们在循环中执行以下System.out.println看下:
在这里插入图片描述
可以看到线程退出了,是可以的,那么我再修改下程序,我让线程睡眠一下:
在这里插入图片描述
还是可以,那么是为什么呢?我们再来每次让线程等待10微妙,首先增加一个方法:

public static void shortWait(long interval){
    long start = System.nanoTime();
    long end;
    do{
        end = System.nanoTime();
    }while(start + interval >= end);
}

然后执行:
在这里插入图片描述
可以看到还是不行,那么每次等待长一点呢,修改成20微妙,看下
在这里插入图片描述
可以看到是可以的,那么这个例子到现在,到底是为什呢?首先,最后一个每次循环等待一个时间片段,当10微妙的时候是不行的,当20微妙的时候可以的,为什么呢,我们先不说为什么可以,我们来看个图
在这里插入图片描述
看上图的JMM线程内存模型是不是有点懂了? 首先我们启动线程1,线程1会创建虚拟机栈,并且创建栈帧来存储线程1的的局部变量表和操作数栈信息,其中falg是全局变量,是从主存加载过来的,在本地内存也就是虚拟机栈中是falg变量的一个副本,首先加载过程是从主存通过指令read读取过来,然后load到虚拟机栈的本地内存中,然后通过执行引擎从用户态切换到内核态,执行我们的while程序,记着,这个时候是从本地内存读取的falg;那么2s过后,线程2启动,线程2也是和线程1一样的方式将falg修改过后,但是这个时候不一样的是修改了falg的值会通过指令assgin回写到主存中;此时,我们的线程1还在使用本地内存的falg的副本,它根本就不知道falg的值已经被修改了,所以这个就涉及到可见性问题。首先我们测试的最后一种方式,每次循环停止20微妙的情况下,是可以退出循环的,为什么呢,因为这个时候缓存失效了,本地内存的缓存falg失效了,又重新重主存加载了一份新的过来,那么这个时候falg值已经发生了变化,所以能退出循环,那为什么10微妙不行,所以这个就是本地缓存的失效时间,简单来说就是,如果长时间没有使用这个缓存值的时候,可能jvm就会重新重主存中将这个值加载过来覆盖新的变量副本。System.out.println:在循环中执行System.out.println为什么能够退出呢?不知道大家有没有看过System.out.println的底层,如果看过,你就会注意到System.out.println的实现有Synchronized

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

也就是说 synchronized 它是通过一种内存屏障的方式来保证变量的可见性的,当falg赋予了可见性的保证过后,那么它是可以被第一时间读取到新的值的;Thread.sleep(0):这个为什么也可以了,我们直接睡眠0s,这个时候只要执行了Thread.sleep,不管你睡眠多少s,都会读到最新的falg值,为什么呢?我在文章的中间记录了线程的上下文切换,什么意思呢?上下文?上下文的意思就是说当切换回来过后会重新加载上下文信息,而Thread.sleep是要让出cpu的执行时间周期权限,那么当时间到了过后又重新加载了上下文,比如falg变量, 所以这个时候falg是false,就会跳出循环,我们关注的不是说Thread.sleep多少s,而且Thread.sleep这个动作已经让出了cpu的执行权限,而当再次唤醒时,是要重新加载上下文信息的,所以Thread.sleep是可以的。我们修改下程序,不加System.out.println,也不加Thread.sleep,也不用每次循环去等待多少微妙,我们只修改下falg变量的修饰,如:

public class T0916 {

    private volatile static boolean falg = true;


    public static void main(String[] args) throws InterruptedException {
        T0916 t0916 = new T0916();
        new Thread(() -> t0916.thread1(),"thread1").start();
        Thread.sleep(2000);
        new Thread(() -> t0916.thread2(),"thread2").start();

    }


    private void thread1(){
        System.out.println(Thread.currentThread().getName()+"开始启动...");
        int i = 1;
        while (falg){
            i++;
        }
        System.out.println(Thread.currentThread().getName()+" 结束 i="+i);
    }

    private void thread2(){
        System.out.println(Thread.currentThread().getName()+"开始启动...");
        falg = false;
        System.out.println(Thread.currentThread().getName()+"执行结束...");
    }
    public static void shortWait(long interval){
        long start = System.nanoTime();
        long end;
        do{
            end = System.nanoTime();
        }while(start + interval >= end);
    }

执行结果:
在这里插入图片描述
是不是可以退出的,所以搞了这么半天其实就是一个变量的可见性的问题,我们在并发编程中很常用的一个关键字volatile,标示我们的这个变量对所有的线程都是可见的,它只能保证可见性,没有办法保证原子性,也就是说所有的线程在使用它或者修改它的时候都可以可见的,可以看到的最新值,但是不能保证其最终值,因为可能其他线程已经修改了它。像synchronized也是能保证可见性的。volatile不仅能保证可见性,它还和内存屏障有关系,还有指令重排等。所以这个例子结合了JMM模型来说就是为了描述清楚并发编程的三大特性之可见性和有序性的解释。volatile是java中的内置关键字,我们从程序代码中是没有办法去查看它的具体实现的,如果我们要看它是如何实现的,就要借助于汇编的指令来查看,汇编这块,我自己也不太懂,所以也就没有办法去研究它的具体实现,但是我们通过java编译的指令去大概查询下它生成的汇编指令,在启动命令输入-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp,就可以看到汇编的一些指令,但是我这边有点问题,没有办法输出,如果输出的话就可以看到一个指令lock addl $0x0,(%rsp);后面有空了去试下看;为了搞懂volatile,我们在硬件层面去分析下,以前有人给我说过我们做程序一定要有机械同感,什么意思呢?我们平时开发程序的时候,根本就不知道这程序性能如何,我们的代码在这个硬件配置上运行,是否能达到性能最优,可能很多人都不知道,只是模糊的概念,说白了现在大多数程序员只是把 功能实现了,这些实现的功能的硬件层面是如何运行的,可能大多数人都不知道,比如文章前所说new Thread和普通的new User有什么区别,可能大多数人都不知道,都不知道创建线程最终是由内核去处理的,所以我们要尽可能的去压榨硬件的性能,让其达到性能最优,我们做程序的一定要有机械同感的思想,思想很重要,比如JMM线程内存模型中,本地虚拟机栈中的栈内存主要保存方法执行的一些栈信息,而全局的变量对象信息在虚拟机栈中是一个副本,那么只有重新加载过来才会有最新的数据,这只是一个例子,我们要理解它的过程,而不是原来是这样;再比如说Mq,大家都知道的,那么不同的服务器是如何能够读取到其他的服务器发过来的消息,就是她最终把消息落盘到了一个指定的地方,大家都去那里拿,这只是一种思想而已,所以为了很好的理解这块,这边我把JMM模型升级到CPU级别,来分析下,比如我的两个线程对同一个变量i进行修改,线程1 i+1,线程2 i+2,如图:
在这里插入图片描述
看了上图,我们来思考一个问题,为什么CPU不直接把i这个变量从主存直接读取到寄存器中,而是要通过高速缓存一级一级的读取到寄存器中呢?因为CPU从主内存中直接读取到寄存器中要花费160多个时钟周期,我之前好像是在那个文章上看到的,而从高速缓存中加载是要快的很多,这个暂不讨论,从上图就可以看到是不是我们的i这个变量最后的值是不确定的,要么1要么2,数据不安全的,但是目前的CPU不可能是这样的,它自己有锁机制,那比如我们锁总线呢?类似于java中的锁,我直接把总线锁了,行不行呢?我们看下图:
在这里插入图片描述
这样是能保证了变量i的安全了,但是这样做是不是CPU设计的多核就毫无用处了,那我要多核CPU干嘛,所以总线锁这个肯定不可取的,但是总线锁也是存在的,如果MESI缓存一致性协议不能使用,好像就是使用的总线锁,我也有点记不清楚了;如果采用缓存一次性协议MESI,那么性能就好很多,但是MESI的锁缓存行cacheLine的大小只有64byte,数据超过了64byte就不能使用MESI或者MOESI,那具体缓存一致性协议MESI是如何做的呢?
在这里插入图片描述
MESI的这个动态图不太好画,简单描述下,就是比如我们的Thread1读取到了i这个变量到CPU的高速缓存区域过后,进行了M独占,那么这个时候我们的总线有个功能交易嗅探机制,嗅探到了Thread1独占了i这个变量,那么这个时候其他线程读取到的i都是I无效的,当Thread1完成了i+1的操作过后,这个时候I就变成了M,而将i回写到了主存过后,那么这个时候每个线程取到的i都是S共享状态了,线程2对i进行加载成了E,然后修改完成过后又是M,这个时候线程1的i是I无效的,最后回写主存,线程1重新读取i,这个时候大家都是共享的,所以这个就是MESI缓存一致性协议,当然这个性能也不是最高,最后提出了一个MOESI,这个和MESI有什么区别呢?就是MESI每次修改数据都要回写主存,其他线程又从主存读取,而MOESI这个协议是直接通过广播机制将修改的值广播到其他核的高速缓存区,其他线程不用从新从主存读取,这个就是MOESE和MESI缓存一致性协议的区别。以上的CPU缓存一致性协议不是默认触发的,因为很多计算过程是不需要启动缓存一致性协议的,只有当我们告诉cpu,需要启动缓存一致性协议才会发送,而在java开发中,只要你将对象或者变量加入了volatile关键字过后,生成的汇编指令为lock addl $0x0,(%rsp),lock就告诉cpu启动缓存一致性协议,lock本身不是内存屏障,但是在缓存一致性协议中起到了内存屏障的作用。总结:java应用程序中声明的对象或者变量如果不加volatile关键字,那么cpu是不会默认启动缓存一致性协议的,当加了volatile关键字过后,汇编指令生成lock指针,告诉CPU要启动缓存一致性协议,这样就保证了每个线程在写的时候都能读到最新的值,但是不能保证原子性,所以cpu的缓存一致性协议在读的时候可以说是原子性的,但是在整个过程中不是原子的,所以volatile是增加了内存屏障,禁止指令重排,从而使每个线程都能读取到最新的值。

【Unity动画优化插件】BT - OptiAnimX —— AAA级动画优化框架深度剖析 BT - OptiAnimX 是一款面向 Unity 的高性能动画优化框架,核心目标是解决多角色动画场景中的性能瓶颈问题。该插件对标《战争机器5》和《巫师3》等 AAA 游戏中的动画优化技术,通过一整套自动化与智能化的优化策略,将动画性能提升与视觉质量控制结合在一起。它适用于从移动端、VR 到高端 PC 的多平台项目,尤其在“多人角色 + 高频动画”的场景中表现突出,例如 MMO、开放世界、动作游戏等。BT - OptiAnimX 本质上是一套“动画性能调度系统”,而不仅仅是优化工具。 阅读详情

相关推荐

保姆级避坑指南:Mid-360雷达到手后,用livox_ros_driver2从接线到出点云的完整流程

本文提供Mid-360雷达到手后的完整使用指南,涵盖从硬件接线、网络配置到livox_ros_driver2驱动的安装与调试,帮助用户避开常见陷阱,实现点云数据的稳定输出与可视化。重点解析了配置文件的参数设置和点云格式选择,确保用户能够高效部署和使用Mid-360雷达。

weixin_30733003的博客 488

并发编程之JMM模型&Volatile详解

微共享: 当多个线程同时去多次去刷新一个缓存行的时候,会导致缓存反复失效,从而降低效率。可以通过填充数据,让多个线程的变量不在同一个缓存行来提高效率 缓存行大小: 64byte 注解:@sun.misc.Contended 要和JVM参数一起使用 前端编译 后端编译 0101010 He...

1 303

告别代码移植烦恼:STM32CubeMX 6.4.0 + STM32F407ZGT6 + YT8512C PHY芯片的LWIP网络配置全攻略

本文详细介绍了如何使用STM32CubeMX 6.4.0配置STM32F407ZGT6与YT8512C PHY芯片的LWIP网络适配,包括硬件特性解析、STM32CubeMX基础配置、专用驱动适配和LWIP协议栈定制化配置,帮助开发者解决PHY芯片差异导致的网络问题。

weixin_28718793的博客 291

并发编程之JMM和Volatile

JMM内存模型,Java并发编程,关键字volaile

polaris645的专栏 278

电磁波成像技术全解析:从γ射线到无线电波的应用实践

本文全面解析电磁波成像技术,从γ射线到无线电波的应用实践。详细介绍了不同波长电磁波的特性与实用场景,包括医疗影像、工业检测、安防监控等领域。特别探讨了X射线、红外热成像、毫米波雷达等前沿技术的实战应用,为读者提供技术选型指南和操作技巧。

weixin_29305337的博客 293

使用RustDesk或者AnyDesk的TCP隧道实现Codesys系列PLC的远程调试和下载

Codesys远程调试

exat500g的博客 7279

并发编程之深入理解JMM&并发三大特性&volatile

并发编程之深入理解JMM&并发三大特性&volatile并发和并行并发三大特性可见性有序性原子性Java内存模型(JMM)JMM定义JMM与硬件内存架构的关系内存交互操作JMM的内存可见性保证volatile的内存语义volatile的特性volatile写-读的内存语义volatile可见性实现原理JMM内存交互层面实现硬件层面实现volatile在hotspot的实现字节码解释器实现模板解释器实现lock前缀指令的作用指令重排序volatile重排序规则volatile禁止重排序场景:J

qq_19375579的博客 1367

ZYNQ PS-PL数据交互方式总结

一,中断 二,IO方式:MIO EMIO GPIO 三,BRAM或FIFO或EMIF (1)通过BRAM实现PL/PS之间交互 PS 的配置如下图所示。使能 M_AXI_GP0 口,将 FCLK_CLK0 设为 100MHz,使能 PL 至 PS 的中断。 AXI BRAM Controller IP 核连接 PS 的 M_AXI_GP0 口和 BRAM,完成 AXI 接口至 BRAM 接口的转换。 添加BRAM,将BRAM设置为双口RAM,将PORTA与AXI B...

wangjie36的博客 1万+

Java内存模型(JMM)和volatile原理

一篇带你弄懂Java内存模型(JMM)和volatile原理,为什么synchronized可以保证可见性,以及volatile的底层原理,如何防止多线程JVM指令重排问题,如何利用syn和volition实现现场安全的单例。

weixin_54232666的博客 1352

基于江协科技STM32创建HAL库工程模板

基于江协科技STM32入门教程创建的HAL库工程模板

2401_82728381的博客 1136

【swjtu】算法实验2.4分析算法时间复杂度

分析给定算法的时间复杂度。 上机实验,绘制不同输入情况下的运行时间曲线,并与(1)中的分析结果进行比较。 撰写相应的实验报告,实验报告内容包括:实验目的、实验任务、 实验环境、实验步骤、实验结果及其分析以及实验总结等部分内容。

qq_52046937的博客 731

JMM模型与volatile详解(一)

JMM模型与volatile详解JMM模型JMM不同于JVM内存区域模型JMM模型图主内存工作内存Java内存模型与硬件内存架构的关系JMM存在的必要性数据同步八大原子操作同步规则分析JMM的可见性、原子性,有序性原子性可见性有序性JMM如何解决原子性&可见性&有序性问题原子性问题可见性问题有序性问题as-if-serialhappens-before 原则volatile关键字volatile的可见性示例不能保证原子性禁止指令重排 JMM模型 Java内存模型(Java Memory Mo

玄霄 574

Langchain cannot connect with Azure SQL Database

Langchain 无法连接到 Azure SQL 数据库

suiusoar 633

深入理解并发编程之volatile与JMM内存模型

Java内存模型(Java Memory Model,JMM)用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果,JMM规范了Java虚拟机与计算机内存是如何协同工作的:规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。

李永志的博客 591

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Java内存模型 &Volatile

笔者的 “多线程- 让程序更高效的运行” 关联文 感谢帮助过我的好基友们,一如既往地发布好文,让技术舞动起来 ■ Java 内存模型  1.1 Java 内存模型 Java线程间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见 JMM是一个抽象的概念,并非真实存在,它涵盖了缓存、写缓冲区、寄存器以及其他的...

dingmei1901的博客 136

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

并发编程(一)计算机模型&volatile关键字详解

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录系列文章目录一、现代计算机理论模型与工作方式1、冯诺依曼计算机模型2、计算机硬件多CPU架构3、缓存一致性协议(MESI)二、什么是线程1、进程与线程2、用户线程和内核线程3、Java线程与系统内核线程关系4、java线程的生命周期三、为什么用到并发?并发会产生什么问题?1、为什么用到并发2、

xiaolinzi176的博客 415

Java内存模型&volatile关键字

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34318956的博客 84

并发编程-02.JAVA内存模型(JMM) 与 volatile 详解

1.Java内存模型 JVM定义了一套在多线程读写共享数据时(成员变量,数组)时,对数据的可见性、有序性和原子性的规则和保障。 1.1.原子性 **定义:**一个操作在CPU中不可以中途暂停再调度,要么全部执行完成,要么全部都不执行 问题: 两个线程对初始值的静态变量一个做自增,一个做自减同样做10000次的结果很可能不是0 解决关键字: synchronized 语法 sychronized(对象) { 需要做原子操作的代码 } 建议: 用sychronized对对象加锁的力度建议大一点(减少加解锁

weixin_49352973的博客 517

JMM&synchronized&volatile详解

系列文章目录 Java内存模型(Java Memory Model简称JMM)是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存

qq1071643762的博客 456

java volatile场景,JAVA锁和volatile的内存语义&volatile的使用场景

JAVA锁的内存语义当线程释放锁时,JMM(Java Memory Model)会把该线程对应的本地内存中的共享变量刷新到主内存中。当线程获取锁时,JMM会将该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。对比锁释放-读取的内存语义与volatile写-读的内存语义可以看出,锁释放与volatile写具有相同的内存语义;锁获取与volatile读具有相同的...

weixin_39784774的博客 132

并发编程之JMM&volatile详解

并发编程之JMM&volatile详解什么是JMM模型?volatile内存语义 什么是JMM模型? \qquadJava内存模型(Java Memory Model简称JMM)是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是

wokoone的博客 286

深入理解JMM+Volatile

计算机多核并发缓存架构 下图是计算机运行架构图:   由于cpu的运行程序速度远大于主存储的速度,所以会在主存RAM和CPU之间加多级高速缓存,缓存的速度接近cpu的运行速度,木桶效应,水能装多少,取决于短板,因此这样会大大提高计算机的运行速度。 java内存模型 定义(摘抄《深入理解+Java+内存模型_程晓明》)   java线程内存模型跟cpu缓存模型类似,是基于cpu缓存模型来...

993

Java 并发编程之JMM概念

线程通信、线程同步 并发编程模型的两个关键问题 线程间如何通信?即:线程之间以何种机制来交换信息 线程间如何同步?即:线程以何种机制来控制不同线程间操作发生的相对顺序 有两种并发模型可以解决这两个问题: 消息传递并发模型 共享内存并发模型 Java 中的并发采用的就是共享内存模型,因为这样 Java 线程之间的整个通信过程对程序员来说是完全透明的。 通信的两种并发模型实现 共享内存并发模型:线程之间共享程序的公共状态,通过写-读内存中的公共状态来进行隐式通信。 消息传递并发模型:线程之间没

qq_44131750的博客 330

由volatile分析jmm内存模型

volatile是java 中的一个关键字,类似synchronized又由不同,这里不再讲述synchronized volatile :一个变量被volatile 修饰之后,线程之间可以共享,就是所谓的线程共享变量 但为什么能够线程共享,又为什么volatile 不像synchronized能保证原子性,这要从java内存模型来说起 java 中,每个线程都是有各自的工作内存,而在进行操作的时候,都是从主内存读取到工作内存中来进行运算或者操作,然后再回写到主内存中 ...

JAVA 1334

并发编程之JMM&volatitle详解

一、JMM模型:Java内存模型(Java Memory Model)是一种抽象的概念,描述的是一组规则/规范,定义了程序中各变量的访问方式。 1、线程创建时JVM会为其创建一个工作内存(栈空间),线程对只能操作工作内存,工作内存中出处着主内存变量的副本拷贝; 2、所有变量都存储在主内存(共享内存区域),线程不能操作主内存的变量,线程间通信(传值)必须通过主内存来完成; 主内存:主要存储Java实例对象 工作内存: 主要存储当前方法所有本地变量信息(主内存变量副本拷贝),包括...

David_bdqn的博客 1311

Java并发编程(八):volatile使用和原理详解

一、背景 大家都知道volatile作为一个“轻量级”的关键字,它能够保证可见性、有序性,但是不能保证原子性。那么它到底是怎么保证可见性和有序性的呢?为什么不能保证原子性呢?我们该如何正确使用volatile呢?下面我们一一进行解释。 二、volatile之可见性 对于可见性,我们在前面的博文已经介绍过了,这里直接出一个实际的例子: public class Test { pub...

黄智霖的博客 2万+

JMM volatile原理

JMM volatile原理

杨宇昌的博客 284
上一篇: 执行引擎、逃逸分析、JIT即时编译
下一篇: Volatile内存屏障&指令重排
Life  journey
博客等级 码龄17年 1万+粉丝 80原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值