Java内存模型原理小结

Java 线程内存模型 了解 Java 的内存模型定义,是 Java 并发编程基本原理的基础知识;从概念上了解线程的私有内存空间和主内存,能够从全局上了解线程是如何进行内存数据的存取操作的;了解线程拥有私有空间的意义,私有空间能够为线程提供独有的数据,其他线程不可干扰;在多线程环境下,主内存操作共享变量需要注意的事项需谨记,数据安全问题很重要;Java 线程也是拥有生命周期的,了解它的生命周期,从宏观上了解线程。定义。 阅读详情

目录

1. Java代码到CPU指令

 2. JVM内存结构/Java内存模型/Java对象模型

2.1 JMM之重排序

2.2 JMM之可见性

2.3 主内存和本地内存

2.4 Happens-Before原则

2.5 volatile关键字

2.5.1 volatile 不适用于a++

2.5.2 volatile适用于纯赋值操作

2.5.3 volatile适用于刷新之前变量的触发器

2.5.4 volatile的两点作用

2.5.5 volatile和synchronized的关系

2.5.6 volatile小结

3. 原子性

4. 单例模式与JMM的联系

4.1 饿汉式写法

4.1.1 饿汉式静态常量

4.1.2 饿汉式静态代码块

4.2 不可用的线程不安全的懒汉式

4.3 线程安全的同步代码块的懒汉式

4.4 线程不安全的同步代码块的懒汉式

4.5 推荐使用:双重检查

 4.6 推荐使用:静态内部类写法

4.7 推荐使用:枚举写法

4.8 单例模式总结

5. 面试常见问题

5.1 什么是Java内存模型?

5.2 volatile和synchronized的异同?

5.3 什么是原子操作?Java中有哪些原子操作?

5.4 生成对象的过程是不是原子操作?

5.5 为什么会有内存可见性问题?

5.6 64位的double和long写入的时候是原子的吗?

5.7 synchronized对于可见性问题的作用


1. Java代码到CPU指令

 

 

 2. JVM内存结构/Java内存模型/Java对象模型

  • JVM内存结构,和Java虚拟机的运行时区域有关;
  • Java内存模型,和Java的并发编程有关;
  • Java对象模型,和Java对象在虚拟机中的表现形式有关。

下图把整个Java虚拟机的运行时区域分为5个部分,这就是JVM内存结构。

 

Java对象模型:

  • Java对象自身的储存模型;
  • JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类;
  • 当我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了对象头以及实例数据。

 

Java内存模型,就是JMM。JMM实际上是一种规范,需要各个JVM的实现来遵守JMM规范,以便于开发者可以利用这些规范,更方便地开发多线程程序。

如果没有JMM,那么很可能经过了不同JVM(比如OpenJDK,Oracle的JDK)的不同规则的重排序之后,会导致不同的虚拟机上运行的结果不一样。

此外,JMM是工具类和关键字的原理。

  • volatile、synchronized、Lock等的原理都是JMM;
  • 通过JMM,开发者只需要用同步工具和关键字就可以开发并发程序。

 

JMM最重要的3点内容分别是:重排序、可见性、原子性。

 

2.1 JMM之重排序

 

public class OutOfOrderExecution {

    private static  int x = 0, y = 0;
    private static int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread one = new Thread(new Runnable() {
            @Override
            public void run() {
                a = 1;
                x = b;

            }
        });

        Thread two = new Thread(new Runnable() {
            @Override
            public void run() {
                b = 1;
                y = a;
            }
        });
        one.start();
        two.start();
        one.join();
        two.join();
        System.out.println("x = " + x + ", y = " + y);
    }
}

第一次输出:

x = 0, y = 1

稍微换一下线程的启动顺序:

 得到结果:

 

 

再通过工具类使得两个线程同时开始:

import java.util.concurrent.CountDownLatch;

/** 演示重排序,“直到达到某个条件才停止” */
public class OutOfOrderExecution {

    private static  int x = 0, y = 0;
    private static int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException {
        int i =0;
        for (;;){
            i++;
            x=0;
            y=0;
            a=0;
            b=0;

            CountDownLatch latch = new CountDownLatch(1);

            Thread one = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        latch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    a = 1;
                    x = b;

                }
            });

            Thread two = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        latch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    b = 1;
                    y = a;
                }
            });
            two.start();
            one.start();
            latch.countDown();
            one.join();
            two.join();

            String result = "第"+i+"次 (" +x+"," +y+")";
            if (x==1&&y==1){
                System.out.println(result);
                break;
            }else {
                System.out.println(result);
            }
        }
    }
}

第N次执行时候出现 :

x=1,y=1

 

所以,什么是重排序:

在线程1内部的两行代码的实际执行顺序和代码在Java文件中的顺序不一致,代码指令并不是严格按照代码语句顺序执行的,它们的顺序被改变了,这就是重排序。这里被颠倒的是y=a和b=1这两行语句。

 

重排序的好处:提高处理速度

 

 

2.2 JMM之可见性

/** 演示可见性问题 */
public class FieldVisibility {

    int a= 1; int b=2;

    private void change(){
        a = 3;
        b = a;
    }

    private void print(){
        System.out.println("b="+b+"; a="+a);
    }

    public static void main(String[] args) {
        FieldVisibility test = new FieldVisibility();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                test.change();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                test.print();
            }
        }).start();
    }
}

会出现 b=3, a=1的情况,

原因是:线程2看了b=3,但是a还没有从线程1同步过来,线程2之看到了初始化时的a=1,这就是可见性问题。

写线程和读线程只能通过共享内存来进行通讯,但可能存在延迟的情况,这就导致了可见性问题的产生。

解决:使用volatile 关键字强迫程序每次都去读取最新的值。

原理:

一旦写线程把x改为1,在读线程读取之前,volatile就会强制行的把x=1更新(flush)到主内存中。

 

为什么会有可见性问题?

因为从内存到CPU之间有多层缓存,从下到上,容量递减,速度递增,不同层次的缓存要拿到对方最新的数据,是有延迟的。

CPU有多级缓存,导致读的数据过期:

  • 告诉缓存的容量比主内存小,但是速度仅次于寄存器,所以在CPU和主内存之间就多了Cache层;
  • 线程间的对于共享变量的可见性问题不是直接由多核引起的,而是由多缓存引起的;
  • 如果所有的核心只有一个缓存,那就不会出现内存可见性问题;
  • 但是每个核心都会讲自己需要的数据读到独占的缓存中,数据修改后也是写入到缓存中,然后等待刷入到主存中,所以会导致有些核心读取的值是一个过期的值。

 

2.3 主内存和本地内存

不同的工作线程都有属于自己的独立工作内存,这些独立的内存之间是不共通的,它们都过Buffer连接主内存进行通讯。

 

主内存和本地内存的关系如何?

JMM有以下规定:

  • 所有变量都存储在主内存中,同时每个线程也有自己独立的工作内存,工作内存中的变量内容是主内存中的拷贝;
  • 线程不能直接读写主内存中的变量,而是只能操作自己专有的工作内存中的变量,然后再同步到主内存中;
  • 主内存是多个线程共享的,但是线程之间不共享工作内存,如果线程间需要通信,必须借助主内存中转来完成。

 

总结:

所有的共享变量存在与主内存中,每个线程有自己的本地工作内存,而且线程读写共享数据也是通过工作内存交换,但是存在延迟的情况,所以导致可见性问题的出现。

 

2.4 Happens-Before原则

 

2.5 volatile关键字

如果对于一个基本变量直接赋值,那就是原子操作。

 

 

2.5.1 volatile 不适用于a++

代码演示:

import java.util.concurrent.atomic.AtomicInteger;

/** volatile不适用a++ */
public class NoVolatile implements Runnable{

    volatile int a ;
    AtomicInteger realA = new AtomicInteger();

    @Override
    public void run() {
        for (int i= 0 ; i <10000; i++){
            a++;
            realA.incrementAndGet();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Runnable r= new NoVolatile();
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(((NoVolatile)r).a);
        System.out.println(((NoVolatile)r).realA);
    }
}

输出:

volatile 修饰的变量或者布尔值,不能依赖以前的状态,否则volatile失效,因为volatile不能做到原子保护。

 

2.5.2 volatile适用于纯赋值操作

代码演示:

import java.util.concurrent.atomic.AtomicInteger;

/** volatile适用场景1 */
public class UseVolatile1 implements Runnable{

    volatile boolean done = false ;
    AtomicInteger realA = new AtomicInteger();

    @Override
    public void run() {
        for (int i= 0 ; i <10000; i++){
            setDone();
            realA.incrementAndGet();
        }
    }

    private void setDone() {
        done = true;
    }

    public static void main(String[] args) throws InterruptedException {
        Runnable r= new UseVolatile1();
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(r);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();

        System.out.println(((UseVolatile1)r).done);
        System.out.println(((UseVolatile1)r).realA);
    }
}

输出:

小结:

适用场景1:boolean flag,如果一个共享变量自始至终只被各个线程赋值,而没有其他的操作,那么就可以用volatile来替代synchronized或者替代原子变量,因为赋值自身是有原子性的,而volatile又保证了可见性,所以足以保证线程安全。

 

2.5.3 volatile适用于刷新之前变量的触发器

 

2.5.4 volatile的两点作用

2.5.5 volatile和synchronized的关系

volatile 可以看做是轻量版的synchronized:

如果一个共享变量自始至终只被各个线程赋值,而没有其他的操作,那么就可以用volatile来替代synchronized或者代替原子变量,因为赋值自身是原子性的,而volatile又保证了可见性,所以就足以保证线程安全。

 

2.5.6 volatile小结

  1. volatile 修饰符适用于以下场景:某个属性被多个线程共享,其中有一个线程修改了次属性,其他线程可以立即得到修改后的值,比如boolean flag;或者作为触发器,实现轻量级同步。
  2. volatile属性的读写操作都是无锁的,它不能替代synchronized,因为它没有提供原子性和互斥性。因为无锁,不需要花费时间在获取锁和释放锁,所以它是低成本的。
  3. volatile只能作用于属性,用volatile修饰属性,这样compilers就不会对这个属性做指令重排序。
  4. volatile提供了可见性,任何一个线程对其的修改将立马对其他线程可见。volatile修饰的属性不会被线程缓存,始终从主存中读取。
  5. volatile提供了happens-before保证,对volatile变量v的写入happens-before所有其他线程后续对v的读操作。
  6. volatile可以使得long和double的赋值是原子的。

 

3. 原子性

原子操作+原子操作 != 原子操作,即原子操作的组合不具备原子性。

在32位上的JVM,long和double的操作不是原子的,但是在64位的JVM上是原子的。

 

4. 单例模式与JMM的联系

单例模式的作用:节省内存和计算,保证结果正确,方便管理。

单例模式适用场景:

4.1 饿汉式写法

优点:写法比较简单,类装载的时候就完成了实例化,

 

4.1.1 饿汉式静态常量

/** 单例模式:饿汉式写法(静态常量) */
public class Singleton1 {

    /** 由于static的缘故,根据JVM的规定,会在加载这个类的时候把INSTANCE实例完毕,
      这就避免了线程同步问题
     */
    private final static Singleton1 INSTANCE = new Singleton1();

    /** 单例模式的构造函数都是私有的,这是希望外界不会来调用 */
    private Singleton1(){

    }

    /** 给外界获得单例模式的方法 */
    public static Singleton1 getInstance(){
        return INSTANCE;
    }
}

 

4.1.2 饿汉式静态代码块

/** 单例模式:饿汉式(静态代码块) */
public class Singleton2  {

    //不做初始化
    private final static Singleton2 INSTANCE;

    //在代码块中做初始化,同样可以通过JVM保证线程安全
    static {
        INSTANCE = new Singleton2();
    }

    //单例模式的构造函数都是私有的,这是希望外界不会来调用 
    private Singleton2(){

    }
    //给外界获得单例模式的方法
    public static Singleton2 getInstance(){
        return INSTANCE;
    }
}

 

4.2 不可用的线程不安全的懒汉式

/** 懒汉式写法,线程不安全 */
public class Singleton3 {

    private static Singleton3 instance;

    private Singleton3(){

    }

    public static Singleton3 getInstance(){
        /** 要用的时候才初始化。但是如果两个线程同时运行到达这一句,都判断是null,
            这时线程1执行 instance = new Singleton3(),线程2也同时 instance = new Singleton3()
            并把实例返回。
            这种情况下就会多次创建实例,不再符合单例的要求,所以这种写法不可用,线程不安全。
         * */
        if (instance == null){
            instance = new Singleton3();
        }
        return instance;
    }
}

 

4.3 线程安全的同步代码块的懒汉式

加上synchronized同步方法,但是不推荐使用,虽然安全,但是效率太低。

/** 单例模式:线程安全,但是不推荐 */
public class Singleton4 {

    private static Singleton4 instance;

    private Singleton4(){

    }

    //synchronized虽然安全,但是效率太低,很多线程不能同时进入这个方法
    public synchronized static Singleton4 getInstance(){
        if (instance == null){
            instance = new Singleton4();
        }
        return instance;
    }
}

 

4.4 线程不安全的同步代码块的懒汉式

也不推荐使用。

public class Singleton5 {
    private static Singleton5 instance;
    private Singleton5(){

    }

    public  static Singleton5 getInstance(){
        if (instance == null){
            /** 意图把需要保护的地方用synchronized同步,但是一旦多个线程来到这一步,
                就无法阻止多个线程创建多个实例。
                线程1获得锁运行完之后释放锁,线程2获得锁再赋值一次,这就创建了多个实例,
                所以线程不安全。
             */
            synchronized (Singleton5.class){
                instance = new Singleton5();
            }
        }
        return instance;
    }
}

 

4.5 推荐使用:双重检查

有点:线程安全,延迟加载,效率高,使用了volatile。

/**  单例模式:双重检查 */
public class Singleton6 {
    /** 这是使用了volatile,真正保证了安全 */
    private volatile static Singleton6 instance;
    private Singleton6(){

    }

    public  static Singleton6 getInstance(){
        if (instance == null){
            synchronized (Singleton6.class){
                if (instance == null){
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }
}

为什么要double-check(双重锁)?

答:线程安全。

为什么这里要用volatile修饰?

答:Java中新建对象不是原子操作,新建对象实际上有3个步骤。第一,先创建空的对象,第二,调用构造方法,可能会有复杂的计算(访问数据库之类的),第三,把创建好的实例赋值给等号左边的引用,这样其他线程就能用这个引用了。

但是,对于CPU和编译器而言,它们是有重排序功能的,我们不知道它们是否会把这个3个步骤进行重排序,因为它们完全可以将其顺序颠倒。如下图所示就是一个颠倒之后的效果,比如先创建一个空的对象,然后直接赋值给左边的引用,最后才去调构造方法去做一些初始化工作。

假设CPU做了这样的重排序,线程1进来,执行完语句之后,左边的引用rs已经不是空了,但本身这个对象虽然不是空的,可对象里面的各种属性可能是没有经过计算和赋值。如果此时线程2进来,检查左边的引用rs是否是空,那么线程2只能看到左边的引用不是空的,rs虽然不是空的,但其内部还没有完全准备完毕,此时线程2会直接跳过新建的过程,直接返回rs,随后就会产生空指针问题(NPE)。

所以要使用volatile修饰,以防止重排序出现。

 

 4.6 推荐使用:静态内部类写法

懒汉式,静态内部类写法,可用,满足同时保证了线程安全,和懒加载的优点,效率也不错。

/** 单例模式:懒汉式,静态内部类写法,可用,效率也不错 */
public class Singleton7 {

    private Singleton7(){

    }

    //静态内部类
    private static class SingletonInstance{
        /**只有当真正休要调用getInstance(),才会初始化这个实例。
          由于JVM内的加载性质,保证了即便多个线程同时去访问“Singleton7 INSTANCE”这个对象,
          也不会创建多个实例了,这样就同时保证了线程安全,和懒加载的优点。
         */
        private static final Singleton7 INSTANCE = new Singleton7();
    }
    
    public  static Singleton7 getInstance(){
        return SingletonInstance.INSTANCE;
    }
}

 

4.7 推荐使用:枚举写法

枚举写法实际上是生产实践中最佳的单例模式的写法!!!

/** 单例模式最佳写法:枚举,枚举本质还是一个类 */
public enum Singleton8 {
    INSTANCE;

    public void whatever(){
        /** whatever()代表这个类中的一些方法 */
    }
}

 

4.8 单例模式总结

哪种单例的实现方案最好?

 

5. 面试常见问题

5.1 什么是Java内存模型?

答:

  1. C语言没有内存模型,但是没有会造成很多的问题和混乱,会导致在不同的处理器上程序执行的结果不一样,无法保证并发安全,所以需要一个规范来定义标准,让多线程程序的运行结果真正做到可预期;
  2. 所以Java内存模型,也就是JMM它是一组规范,用来帮助CPU、JVM以及开发者之间进行很好的合作,能够更好的避免线程安全问题,开发者利用JMM规范可以更快更方便地开发多线程程序;
  3. 如果没有JMM作为规范,那么很可能经过了不同地JVM的不同规则的重排序之后,导致不同的虚拟机上运行的结果不一样;
  4. 并且,JMM还是volatile、synchronized、Lock等的原理基础,有了JMM,开发者就只需要用同步工具和关键字就可以开发;
  5. 此外,JMM最重要的3点内容就是重排序、内存可见性、和原子性;
  6. 重排序是指代码实际执行顺序和代码在Java文件中的顺序不一致;重排序有其优点,比如提高处理速度;重排序有3种情况,分别是编译器优化,指令重排序,内存的“重排序”(强调有双引号);
  7. 其中,指令重排序是CPU的优化行为,和编译器优化很相似,是通过乱序执行的技术,来提高执行效率;所以就算编译器不发生重排序,CPU也可能对指令进行重排序,所以开发中要考虑到重排序带来的问题和后果;
  8. 实际上内存系统不存在重排序,但是内存会带来看上去和重排序很像的效果;因为由于内存有缓存的存在,这在JMM表现位主存和(线程运行专属的)本地内存,由于主存和本地内存的不一致,会使得程序表现出乱序的行为;
  9. 比如:如果发现内存缓存不一致,比如有个变量a=0,线程1修改了a的值,但是修改后没有及时写回主存,所以线程2看不到刚刚线程1对a的修改,认为a还是等于0;同样道理,线程2修改了b的值,线程1也看不到线程2的修改;由此引出内存可见性问题;
  10. 为什么会有可见性问题呢,因为CPU为了加快执行速度,在内存和CPU之间,加了很多缓存层;这些缓存层分别会把下面一级的缓存的部分内容取过来,随着层次的逐渐上升,逐渐接近CPU,速度也越快,容量也越小;正是因为多个CPU之间,它们不同时共享高速缓存,所以就导致了可见性问题;这就引出了JMM对于整个内存的抽象;
  11.  Java作为高级语言,屏蔽了很多底层细节,并且用JMM定义了一套读写内存数据的规范,抽象了主内存和本地内存的概念;主要抽象为两个部分,最主要的是主内存部分,所有的变量都是存储在主内存中,而每一个线程都会有自己独立的工作内存,工作内存中的变量内容是主内存中的拷贝;线程与线程之间不能直接进行通讯,要通过主内存中转来完成,所以线程读写共享数据也是通过这样的方式完成,就导致了可见性问题;
  12. 简单来说,线程间对于共享变量的可见性问题是由多级缓存引起,如果所有的核心都只用一个缓存,就不会出现可见性问题;
  13. 解决可见性问题,需要一个happens-before原则,也就是在连续的时间上,动作A发生在动作B之前,B能保证看见A,这就是happens-before原则;
  14. 原子性

 

5.2 volatile和synchronized的异同?

volatile 可以看做是轻量版的synchronized:

如果一个共享变量自始至终只被各个线程赋值,而没有其他的操作,那么就可以用volatile来替代synchronized或者代替原子变量,因为赋值自身是原子性的,而volatile又保证了可见性,所以就足以保证线程安全。

  1. volatile 修饰符适用于以下场景:某个属性被多个线程共享,其中有一个线程修改了次属性,其他线程可以立即得到修改后的值,比如boolean flag;或者作为触发器,实现轻量级同步。
  2. volatile属性的读写操作都是无锁的,它不能替代synchronized,因为它没有提供原子性和互斥性。因为无锁,不需要花费时间在获取锁和释放锁,所以它是低成本的。
  3. volatile只能作用于属性,用volatile修饰属性,这样compilers就不会对这个属性做指令重排序。
  4. volatile提供了可见性,任何一个线程对其的修改将立马对其他线程可见。volatile修饰的属性不会被线程缓存,始终从主存中读取。
  5. volatile提供了happens-before保证,对volatile变量v的写入happens-before所有其他线程后续对v的读操作。
  6. volatile可以使得long和double的赋值是原子的。

 

5.3 什么是原子操作?Java中有哪些原子操作?

 

5.4 生成对象的过程是不是原子操作?

生成对象的过程:

1)新建一个空的Person对象;

2)把这个对象的地址指向P;

3)执行Person的构造函数。

这三个过程不能保证它们的原子性。

 

5.5 为什么会有内存可见性问题?

多层缓存会造成数据同步延迟的问题。

 

5.6 64位的double和long写入的时候是原子的吗?

答:Java并没有规定它们一定是原子的,因为它们是64位的,写入的时候,可能会出现前32位和后32位错位的情况。但是在实际的商用JDK中,这个问题已经被考虑过并且解决了。

 

5.7 synchronized对于可见性问题的作用

synchronized也可以达到happens-before的效果。synchronized不仅防止了一个线程在操作某对象时受到其他线程的干扰,同时还保证了修改之后,可以立即被其他线程所看到,因为如果其他线程看不到,那也会有线程安全问题。

 

【飞腾平台安装windows arm 11系统】 本文提供国内的百度网盘链接供下载使用:链接: https://pan.baidu.com/s/1_cftVdoX40k0M6zVTmFq8Q?3)快速迁移虚拟机技巧:如果是迁移已经安装好的虚拟机的使用场景,除了导出镜像外,也可直接复制安装好虚拟机的整个文件夹到指定的存储目录,选定好其中的任意qcow2文件后即可快速启动。可在飞腾软件平台点击网盘链接获取对应系统的deb包(麒麟和统信)以及phyvirt·app,下载地址:https://www.phytium.com.cn/developer/ 阅读详情

相关推荐

Spring Boot日志可视化实战:5分钟搞定Promtail采集与Grafana查询(避坑指南)

本文详细介绍了如何通过Promtail和Grafana实现Spring Boot日志的可视化查询,提供从日志采集到高效查询的全流程实战指南,重点解决部署中的常见问题。内容包括Logback配置、Promtail日志采集、Grafana的LogQL查询技巧及生产环境优化建议,帮助开发者快速定位和解决日志问题。

weixin_42526015的博客 106

Java内存模型原理,你真的理解吗?

这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关实现规则,环环相扣,希望读者看完这篇文章后能对 Java 内存模型体系产生一个相对清晰的理解,知其然知其所以然。 内存模型产生背景 在介绍 Java 内存模型之前,我们先了解一下物理计算机中的并发问题,理解这些问题可以搞清楚内存模型产生的背景。 物理机遇到的并发问题与虚拟机中的情况有不少相似之处,物理机的解决方案对虚拟机的实现有相当的参...

java1856905的博客 1096

锐捷交换机路由器配置全攻略:从基础命令到高级功能(附常用命令速查表)

本文提供锐捷交换机路由器配置全攻略,涵盖从基础命令到高级功能的详细指南,包括VLAN划分、DHCP配置、链路聚合等实用技巧,并附常用命令速查表,帮助网络管理员快速掌握锐捷设备运维。

weixin_29285605的博客 518

Java多线程-锁机制(volatile、synchronized、CAS)

并发编程的三大特性:可见性、原子性、有序性 volatile保证可见性与部分有序性,但是不保证原子性,保证原子性需要借助synchronized这样的锁机制 volatile(最底层:lock add) ●保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的,volatile关键字会强制将修改的值立即写入主存。 ●禁止进行指令重排序 volatile的可见性 Java内存模型 JMM Java内存模型,是java虚拟机规范中所定义的⼀种内存模型,Java

九曜 5106

《涨知识啦29》-DBR的工作原理及其在VCSEL中的作用

分布式布拉格(Distributed Bragg Reflection,简称DBR)反射镜是垂直腔面发射激光器(Vertical-Cavity Surface-Emitting Laser,简称VCSEL)的重要组成部分。典型的DBR反射镜是由1/4ni波长厚度的高折射率和低折射率材料交替组成(ni是该层的折射率)。其反射率取决于组成对数、边界条件以及折射率差。 图1 布拉格反射镜的工作原理 DBR反射镜的工作原理如图1所示。根据反射原理,当光从光疏介质n1射向光密介质n2(折射率n2>n1)时,反

simucal的博客 7983

I2C通信协议SCL与SDA引脚特性:核心要点总结

深入讲解I2C通信协议中SCL和SDA引脚的工作机制与电气特性,帮助理解i2c通信协议的稳定传输原理,掌握上拉电阻配置与信号时序控制要点。

weixin_42103128的博客 2731

喂,不是吧?一个Volatile把你难住了

Volatile可能是面试里面必问的一个话题吧,对他的认知很多朋友也仅限于会用阶段,今天我们换个角度去看看。 在正式聊volatile之前呢,我们先说说另外一个东西 Java内存模型 JMM:Java内存模型,是java虚拟机规范中所定义的一种内存模型,Java内存模型是标准化的,屏蔽掉了底层不同计算机的区别(注意这个跟JVM完全不是一个东西,只有还有小伙伴搞错的)。 随着计算机的CPU的飞速发展,CPU的运算能力已经远远超出了从主内存(运行内存)中读取的数据的能力,为了解决这个问题,CPU厂商设计出了CP

dabaoshiwode的博客 185

liquibase扩展支持达梦数据库

近期项目中需要支持国产数据库,需要liquibase底层支持达梦数据库,记录下操作的详细步骤以便分享。 1.liquibase支持的数据库很多如MySQL, PostgreSQL, Oracle, Sql Server, DB2。但是达梦数据库并不支持,所以需要新建DMDatabase类来适配达梦数据库。达梦语法和Oracle很像所以只需要复制liquibase-core下的Oracle Database做些修改就可了。以下为需要修改的地方 public class DMDatabase extends A

qq_43677719的博客 5472

java计算两个数的差_全面理解Java内存模型

Java 内存模型的抽象在 java 中,所有实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享(本文使用“共享变量”这个术语代指实例域,静态域和数组元素)。局部变量(Local variables),方法定义参数(java 语言规范称之为 formal method parameters)和异常处理器参数(exception handler parameters)不会在线程之间共享,它...

weixin_35762215的博客 775

uniapp - [全端兼容] 实现精美Editor富文本编辑器功能,uniApp全平台兼容的富文本插件组件用户可上传图片及对接服务器存储文件等,手机移动端H5网页富文本/小程序富文本/App富文本!

uni-app,h5移动端网页,微信小程序,支付宝小程序,Android安卓,苹果ios,app,nvue,uniapp富文本编辑器,uniapp中如何实现各平台都兼容的富文本编辑器,Editor富文本插件,uniApp微信小程序富文本,UNI-APP安卓苹果ios系统App端富文本组件,editor富文本编辑器如何实现上传图片,图片上传后不显示怎么办,如何配置图像上传功能,富文本图片上传怎么传到服务器中,获取图片路径,editor插入图片,富文本编辑器上传图片,报错怎么办,跨域CORS处理,配置不行无效

🏆 前端领域优质创作者 1万+

56、Cortex - M33处理器浮点单元(FPU)的使用与优化

本文详细介绍了Cortex-M33处理器浮点单元(FPU)的使用与优化方法,涵盖单双精度浮点计算的选择、编译器命令行配置、ABI模式对比、特殊FPU模式设置(如Flush-to-Zero、Default NaN)、浮点异常处理机制以及FPU电源管理策略。结合代码示例和流程图,提供了在不同应用场景下的最佳实践建议,并总结了常见问题及解决方案,帮助开发者提升嵌入式系统中浮点运算的性能、精度与能效。

bread的博客 154

类加载时是否会执行静态代码块?静态代码块的骚操作。

类加载时是否会执行静态代码块?杂谈问答解总结 杂谈 厦门的口罩都卖完了,前几天都开始摇号卖口罩了,太难了,今天是大年初十,可比去年大年初一还冷清,而且随着疫情的发展,我明显能感觉到周围的朋友情绪不断的低落,希望疫情早日过去,武汉加油!中国加油! 问 类加载时是否会执行静态代码块? 答 不一定。类加载的方式有多种,并不是每一种方式都能执行静态代码块 解 类加载的三种方式(常用方式) 1 new ...

胡歌粉丝的博客 2457

什么是 SAP S/4HANA 的 VDM Layering Architecture 的 CDS Hierarchy View

当涉及SAP S/4HANA的CDS Hierarchy View时,我们正在谈论一种高级数据建模技术,用于构建层次结构化的数据视图,以支持树状和分层数据的处理和分析。CDS(Core Data Services)是SAP的一种数据建模和定义语言,而CDS Hierarchy View则是CDS的一个重要组成部分,用于处理层次数据,如组织结构、产品分类、地理区域等。首先,您可以使用CDS语言创建一个新的CDS Hierarchy View,定义产品分类的层次结构。您还可以定义其他与库存相关的属性。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 390

Java内存模型 -底层原理

【声明】:本篇文章来自本人gitee仓库搬运至优快云,https://gitee.com/genmers/md-notes 文章目录一、底层原理1.1 从Java代码到CPU指令1.2 重点向下钻研二、自顶向下的好处三、JVM内存结构(和Java虚拟机运行时区域有关)3.1 一图胜千言四、Java内存模型(和Java的并发编程有关)五、Java对象模型(和Java对象在虚拟机中的表现形式有关)5.1 一图胜千言5.2 Java对象自身的存储模型5.3 OOP-KIass Model六、JMM是什么6.1

Genmer的博客 420

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

深入学习Java内存模型(Java面试问题)

文章目录一、简述Java内存模型二、CPU和缓存一致性三、Java内存模型的实现 一、简述Java内存模型 Java内存模型(Java Memory Model,JMM)是java虚拟机规范定义的,用来屏蔽掉java程序在各种不同的硬件和操作系统对内存的访问的差异,这样就可以实现java程序在各种不同的平台上都能达到内存访问的一致性。 可以避免像c++等直接使用物理硬件和操作系统的内存模型在不同操作系统和硬件平台下表现不同,比如有些c/c++程序可能在windows平台运行正常,而在linux平台却运行有

末尾卡片和博主交流学习! 2659

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Java线程内存模型

1. 前言 本节内容是从操作系统的层面谈并发,我们需要掌握如下内容: 了解 Java 的内存模型定义,是 Java 并发编程基本原理的基础知识; 从概念上了解线程的私有内存空间和主内存,能够从全局上了解线程是如何进行内存数据的存取操作的; 了解线程拥有私有空间的意义,私有空间能够为线程提供独有的数据,其他线程不可干扰; 在多线程环境下,主内存操作共享变量需要注意的事项需谨记,数据安全问题很重要; Java 线程也是拥有生命周期的,了解它的生命周期,从宏观上了解线程。 2.什么是 Java 的内存模型 定义

Gonelin的博客 497

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Java内存模型

Java并发编程的艺术笔记 并发编程的挑战 Java并发机制的底层实现原理 Java内存模型 Java并发编程基础 Java中的锁的使用和实现介绍 Java并发容器和框架 Java中的12个原子操作类介绍 Java中的并发工具类 Java中的线程池 Executor框架 目录 内存模型基础 volatile的内存语义 锁的内存语义 final域的内存语义 happens-b...

男人要霸气的博客 188

[Java并发与多线程](十一)Java内存模型——底层原理

Java内存模型——底层原理1、底层原理2、三兄弟:JVM内存结构 VS Java内存模型 VS Java对象模型2.1、JVM内存结构:和Java虚拟机的运行时区域有关2.2、Java内存模型-JMM(*):和Java并发编程有关2.3、Java对象模型:和Java对象在虚拟机中的表现形式有关3、JMM是什么?3.1、什么是重排序?3.2、重排序的好处:提高处理速度3.3、 重排序的3种情况:编译器优化、CPU指令重排、内存的"重排序"3.4、可见性3.4.1、为什么会有可见性问题?3.4.2、JMM的抽

xujunson的博客 433

【并发编程】1 synchronized底层实现原理、Java内存模型JMM;可重入、不可中断、monitor、CAS、乐观锁和悲观锁;对象内存结构、Mark Word、synchronized锁升级

【并发编程】synchronized底层实现原理、Java内存模型JMM、可见性、原子性、有序性;计算机结构、CPU缓存;可重入、不可中断、monitor、CAS、乐观锁和悲观锁;对象的内存结构、Mark Word、synchronized锁升级,无锁--》偏向锁--》轻量级锁–》重量级锁、锁清除、锁粗化、平时写代码如何对synchronized优化

qq_44700578的博客 1083

并发(三、Java内存模型)

JMM简介、原子性/可见性/有序性、指令重排、指令重排限制(happens-before、as-if-serial、内存屏障)、volatile

天行健,君子以自强不息 961

03. Java 线程内存模型

了解 Java 的内存模型定义,是 Java 并发编程基本原理的基础知识;从概念上了解线程的私有内存空间和主内存,能够从全局上了解线程是如何进行内存数据的存取操作的;了解线程拥有私有空间的意义,私有空间能够为线程提供独有的数据,其他线程不可干扰;在多线程环境下,主内存操作共享变量需要注意的事项需谨记,数据安全问题很重要;Java 线程也是拥有生命周期的,了解它的生命周期,从宏观上了解线程。定义。

这是一个web全栈开发的博客,取其精华去其糟粕,争取让大家一看就懂,一学就会,一用就成~ 906

java内存模型小结

java线程之间的通信由java内存模型控制,Java内存模型(JMM)决定一个线程对共享变量的写入何时对其他线程可见,为了更好的学习java多线程,我们有必要了解一下java内存模型,在了解java内存模型之前,需要先了解一些其他必要的概念。 指令重排序 在执行程序时,为了提高性能,编译器和处理器可能对指令做重排序。但是,JMM确保在不同的编译器和处理器上,通过插入特定类型dMemo

u010593947的博客 378

jmm java内存模型

jvm详解 1、CPU执行程序: ​ 从内存中取数据存到高速缓存中CPU从高速缓存中取数据,进行运算,运算完成将结果存到高速缓存中,高速缓存将数据刷新到内存中,CPU执行任务的最小单位是线程,四核八线程指的是CPU能并行执行八个线程, 2、并行和并发 并发:指两个或多个事件在同一个时间段内发生。 并行:指两个或多个事件在同一时刻发生(同时发生)。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-btIc0oOP-1607086543447)(C:\Users\lilwi

qq_37375501的博客 191

Java synchronized可见性_Java内存可见性,Java内存模型,synchronized原理

JVM内存结构、Java对象模型和Java内存模型分别解释:JVM内存结构由Java虚拟机规范定义。描述的是Java程序执行过程中,由JVM管理的不同数据区域。 各个区域有其特定的功能。内存.pngJDK1.8之前使用永久代实现方法区,1.8之后改用本地内存中实现的元空间代替,把1.8之前的永久代剩余内容全部转移到元空间中.Java对象模型而Java对象在JVM中的存储也是有一定的结构的。于Jav...

weixin_39623620的博客 183

五年Java复习小结

整理总结-5年

weixin_43999182的博客 1004

Java内存模型——JMM总结

JMM是什么? 是一种规范,是关键字和工具类的原理,各JVM的实现都需要遵循它 JVM内存结构 VS Java内存模型 VS Java对象模型【自己过】 为什么需要JMM? ◇ 运算依赖处理器,不同处理器结果可能不一样 ◇ 无法保证并发安全 ◇ 需要一个标准,让多线程的结果可预期 ◇ C语言不存在内存模型概念 JMM最重要的三点内容 可见性 { 为什么会有可见性问题? CPU有多级缓存,导致读的数据过期 每个核心都会将需要的数据读到独立缓存中 数据修改后也是先写入到缓存中,再等待时机flush到主存

Jaychan的博客 238
上一篇: Java之Synchronized小结
下一篇: 利用UncaughtExceptionHandler全局处理线程异常
专业逮虾户
博客等级 码龄7年 1粉丝 10原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值