Volatile关键字

news2024/11/16 1:45:10

Volatile关键字和JMM内存模型

  • 一JUC并发包API 包介绍
  • 二JMM(Java Memory Model)
  • 三 volatile关键字
    • 3.1.可⻅性
      • 3.1.1.问题演示
        • 3.1.1.1案例代码
        • 3.1.1.2.案例分析
      • 3.1.2.volatile 保证可见性演示
        • 3.1.2.1对number添加了volatile修饰
        • 3.1.2.2运⾏结果是:
    • 3.2.原⼦性
      • 3.2.1.不保证原子性问题演示
        • 3.2.1.1.案例代码
        • 3.2.1.2.案例分析
      • 3.2.2.保证原子性演示--对 addPlusPlus()⽅法加锁
        • 3.2.2.1.案例代码
        • 3.2.2.2.案例分析
      • 3.2.3.保证原子性演示--使用AtomicInteger类
        • 3.2.3.1.案例代码
        • 3.2.3.2.案例分析
    • 3.3.有序性
      • 3.3.1.指令重排问题演示
        • 3.3.1.1.案例代码
        • 3.3.1.2.案例分析
      • 3.3.2.解决指令重排问题演示
        • 3.3.2.1.案例代码
        • 3.3.2.2.案例分析
  • 四 哪些地⽅⽤到过volatile?
    • 4.1.单例模式的安全问题
      • 4.1.1传统单线程方式
        • 4.1.1.1案例代码
        • 4.1.1.2案例分析
      • 4.1.2.多线程方式
        • 4.1.2.1.案例代码
        • 4.1.2.2.案例分析
      • 4.1.3.多线程方式--synchronized加锁
        • 4.1.3.1.案例代码
        • 4.1.3.2.案例分析
      • 4.1.4.多线程方式--DCL(Double Check Lock)双端检查模式
        • 4.1.4.1.案例代码
        • 4.1.4.2.案例分析
        • 4.1.4.3.解决方案

一JUC并发包API 包介绍

在这里插入图片描述
java.util.concurrent:(juc)

  1. 并发与并⾏的不同?
  1. 并发,如同,秒杀⼀样,多个线程访问同⼀个资源
  2. 并⾏,⼀堆事情 ⼀块去做,如同,⼀遍烧热⽔,⼀个拆⽅便⾯包装

java.util.concurrent.atomic

  1. AtomicInteger 原⼦性引⽤

java.util.concurrentlocks

  1. Lock接⼝
  2. ReentrantLock 可重⼊锁
  3. ReadWriteLock 读写锁

二JMM(Java Memory Model)

JMM是指Java内存模型,不是JVM,不是所谓的栈、堆、⽅法区。每个Java线程都有⾃⼰的⼯作内存。操作数据,⾸先从主内存中读,得到⼀份拷⻉,操作完毕后再写回到主内存。

1、由于JVM运⾏程序的实体是线程,⽽每个线程创建时JVM都会为其创建⼀个⼯作内存(有些地⽅成为栈空间),⼯作内存是每个线程的私有数据区域,⽽Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在⼯作内存中进⾏。
2、⾸先要将变量从主内存拷⻉到⾃⼰的⼯作内存空间,然后对变量进⾏操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量。
3、各个线程中的⼯作内存中存储着主内存中的变量副本拷⻉,因此不同的线程间⽆法访问对⽅的⼯作内存,线程间的通信 (传值)必须通过主内存来完成。

期简要访问过程如下图:
在这里插入图片描述

JMM可能带来可⻅性、原⼦性和有序性问题
所谓可⻅性,就是某个线程对主内存内容的更改,应该⽴刻通知到其它线程。
所谓原⼦性,是指⼀个操作是不可分割的,不能执⾏到⼀半,就不执⾏了。
所谓有序性,就是指令是有序的,不会被重排。

三 volatile关键字

在这里插入图片描述
volatile关键字是Java提供的⼀种轻量级同步机制。

  • 它能够保证可⻅性和有序性
  • 但是不能保证原⼦性
  • 禁⽌指令重排

3.1.可⻅性

就是某个线程对主内存内容的更改,应该⽴刻通知到其它线程
在这里插入图片描述

3.1.1.问题演示

3.1.1.1案例代码

/**
 * volatile关键字是Java提供的一种轻量级同步机制。
 * <p>
 * 它能够保证 可见性 和 有序性
 * 但是不能保证 原子性
 * 禁止指令重排(编辑器优化的重排、指定并行的重排、内存系统的重排)
 */
class KjxData {
     int number = 0;
     public void setTo60() {
        this.number = 60;
    }
}

public class KjxDemo {
    public static void main(String[] args) {
        volatileVisibilityDemo();
    }
    
    //volatile可以保证可见性,及时通知其它线程主物理内存的值已被修改
    private static void volatileVisibilityDemo() {
        System.out.println("可见性测试");
        //资源类
        KjxData KjxData = new KjxData();
        //启动一个线程操作共享数据
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 执行");
            try {
                TimeUnit.SECONDS.sleep(3);
                KjxData.setTo60();
                System.out.println(Thread.currentThread().getName() + "\t 更新number值: " + KjxData.number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "ThreadA").start();
        while (KjxData.number == 0) {
            //main线程持有共享数据的拷贝,一直为0
        }
        System.out.println(Thread.currentThread().getName() + "\t main获取number值: " + KjxData.number);
    }

}

3.1.1.2.案例分析

在这里插入图片描述
KjxData类是资源类,⼀开始number变量没有⽤volatile修饰,所以程序运⾏的结果是:
在这里插入图片描述
虽然⼀个线程把number修改成了60,但是主内存中的变量值并没有修改,ThreadB线程持有的仍然是最开始的0,所以⼀直循环,程序不会结束。

3.1.2.volatile 保证可见性演示

3.1.2.1对number添加了volatile修饰

/**
 * volatile关键字是Java提供的一种轻量级同步机制。
 * <p>
 * 它能够保证 可见性 和 有序性
 * 但是不能保证 原子性
 * 禁止指令重排(编辑器优化的重排、指定并行的重排、内存系统的重排)
 */
class KjxData {
   volatile   int number = 0;
     public void setTo60() {
        this.number = 60;
    }
}

3.1.2.2运⾏结果是:

在这里插入图片描述
添加了volatile修饰修饰后,ThreadA在工作内存中对变量值修改后同时也修改共享内存中的变量值通知其他线程变量值已修改。在线程A修改了变量值在其他线程中是可见的。

3.2.原⼦性

不可分割,完整性,也即某个线程正则做某个具体业务时,中间不可以被加塞或者被分割。需要整体完整,要么同时成功,要么同时失败。

3.2.1.不保证原子性问题演示

假设有这样一个需求:模拟20个并发,每个线程将同一变量循环加1000次。我们预期的结果是20000。

3.2.1.1.案例代码

/**
 * volatile关键字是Java提供的一种轻量级同步机制。
 * <p>
 * 它能够保证 可见性 和 有序性
 * 但是不能保证 原子性
 * 禁止指令重排(编辑器优化的重排、指定并行的重排、内存系统的重排)
 */
class MyData {
    //定义一个遍历,在方法中++操作
    volatile int number = 0;

    public void addPlusPlus() {
        number++;
    }
}

public class VolatileDemo {
    public static void main(String[] args) {
        atomicDemo();
    }

    /**
     * 模拟20个并发,每个线程将同一变量循环加1000次。我们预期的结果是20000。
     *
     * @date 2023/3/9
     */
    private static void atomicDemo() {
        System.out.println("原子性测试");
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addPlusPlus();
                }
            }, String.valueOf(i)).start();
        }
        //为了达到理想结果,严谨性需要添加如下判断
        //当前执行的程序中线程存活数大于2说明当前任务没执行完
        //一个程序中有两个固定线程(main和gc线程)
        while (Thread.activeCount() > 2) {
           //表示让其他线程继续执行
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t int类型最终number值: " + myData.number);
    }
}

3.2.1.2.案例分析

在这里插入图片描述
通过多次测试发现很难达到20000,程序看着也没有任何问题但结果就是不符合预期。这是因为并发操没有保证原子性,++的过程出现了数据覆盖,导致结果使用小于预期。
volatile并不能保证操作的原⼦性。这是因为,⽐如⼀条number++的操作,会形成3条指令查看对应的二进制指令代码如下:
在这里插入图片描述
在java代码中看似是一条语句其实编译后在正真操作时是3条指令,在3条指令执行的过程中cpu是并行的,所以在并行并发操作的过程中就会出问题。
在这里插入图片描述

假设有3个线程,分别执⾏number++,都先从主内存中拿到最开始的值,number=0,然后三个线程分别进⾏操作。假设线程0执⾏完毕,number=1,也⽴刻通知到了其它线程,但是此时线程1、2已经拿到了number=0,所以结果就是写覆盖,线程1、2将number变成1。

解决的⽅式就是:

  1. 对 addPlusPlus()⽅法加锁。
  2. 使⽤ java.util.concurrent.AtomicInteger类。

3.2.2.保证原子性演示–对 addPlusPlus()⽅法加锁

3.2.2.1.案例代码

在++操作的代码块中添加synchronized关键字加锁

class MyData {
    //定义一个遍历,在方法中++操作
    volatile int number = 0;

    public synchronized void addPlusPlus() {
        number++;
    }
}

3.2.2.2.案例分析

在这里插入图片描述
加了synchronized关键后代码是同步的其他线程是不能执行的,那么结果就可以得到保证。
引发问题:
synchronized在方法中一锁,那么在高并发的情况下,一个业务方法被锁死那性能必然大打折扣。所以说在高并发的情况下不能用synchronized关键字

3.2.3.保证原子性演示–使用AtomicInteger类

使⽤ java.util.concurrent.AtomicInteger类。
在这里插入图片描述在这里插入图片描述

3.2.3.1.案例代码

1、创建原子型变量
2、使用atomicInteger.getAndIncrement()方法实现自增
3、调用方法


class MyData {
    volatile int number = 0;

    public void addPlusPlus() {
        number++;
    }

    //创建一个具有初始值 0的新原子整数
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addAtomic() {
        //自增操作 以原子方式递增当前值的 1
        atomicInteger.getAndIncrement();
    }

}

public class VolatileDemo {
    public static void main(String[] args) {
        atomicDemo();
    }

    /**
     * 模拟20个并发,每个线程将同一变量循环加1000次。我们预期的结果是20000。
     *
     * @date 2023/3/9
     */
    private static void atomicDemo() {
        System.out.println("原子性测试");
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addPlusPlus();
                    //原子型操作
                    myData.addAtomic();
                }
            }, String.valueOf(i)).start();
        }
        //为了达到理想结果,严谨性需要添加如下判断
        //当前执行的程序中线程存活数大于2说明当前任务没执行完
        //一个程序中有两个固定线程(main和gc线程)
        while (Thread.activeCount() > 2) {
            //表示让其他线程继续执行
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t int类型最终number值: " + myData.number);
        System.out.println(Thread.currentThread().getName() + "\t AtomicInteger类型最终number值: " + myData.atomicInteger);
    }
}

3.2.3.2.案例分析

可⻅,由于Volatile不能保证原⼦性,出现了线程重复写的问题,最终结果⽐20000⼩。⽽ AtomicInteger可以保证原⼦性
在这里插入图片描述

3.3.有序性

计算机在执⾏程序时,为了提⾼性能,编译器和处理器常常会对指令做重排,⼀般分以下三种:在这里插入图片描述

1、单线程环境⾥⾯确保程序最终执⾏结果和代码顺序执⾏的结果⼀致;
2、处理器在进⾏重排序时必须要考虑指令之间的数据依赖性;
3、多线程环境中线程交替执⾏,由于编译器优化重排的存在,两个线程中使⽤的变 量能否保证⼀致性是⽆法确定的,结果⽆法预测。

volatile可以保证有序性,也就是防⽌指令重排序
所谓指令重排序,就是出于优化考虑,CPU执⾏指令的顺序跟程序员⾃⼰编写的顺序不⼀致。就好⽐⼀份试卷,题号是⽼师规定的,是程序员规定的,但是考⽣(CPU)可以先做选择,也可以先做填空。

int x = 11; //语句1 
int y = 12; //语句2
x = x + 5; //语句3
y = x * x; //语句

以上例⼦,可能出现的执⾏顺序有1234、2134、1342,这三个都没有问题,最终结果都是x = 16, y=256。但是如果是4开头,就有问题了,y=0。这个时候就不需要指令重排序

3.3.1.指令重排问题演示

观看下⾯代码,在多线程场景下,说出最终值a的结果是多少? 5或者6
我们采⽤ volatile 可实现禁⽌指令重排优化,从⽽避免多线程环境下程序出现乱序执⾏的现象

3.3.1.1.案例代码


/**
 * volatile可以保证 有序性,也就是防止 指令重排序。
 *
 * 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致;
 * 处理器在进行重排序时必须要考虑指令之间的数据依赖性;
 * 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。
 */
public class ResortSeqDemo {

    int a=0;
    boolean flag=false;
    /*
    多线程下flag=true可能先执行,还没走到a=1就被挂起。
    其它线程进入method02的判断,修改a的值=5,而不是6。
     */
    public void method01(){
        a=1;
        flag=true;
    }
    public void method02(){
        if (flag){
            a+=5;
            System.out.println("*****retValue: "+a);
        }
    }

    public static void main(String[] args) {
        ResortSeqDemo resortSeq = new ResortSeqDemo();
        new Thread(()->{resortSeq.method01();},"ThreadA").start();
        new Thread(()->{resortSeq.method02();},"ThreadB").start();
    }
}

3.3.1.2.案例分析

在这里插入图片描述
通过多次结果发现一直是6,这就有疑问了不是说可能会出现指令重排吗?我们认真思考一下,在 多线程下flag=true可能先执行,还没走到a=1就被挂起。其它线程进入method02的判断,修改a的值=5,而不是6。

3.3.2.解决指令重排问题演示

使用volatile来修饰变量即可

3.3.2.1.案例代码

/**
 * volatile可以保证 有序性,也就是防止 指令重排序。
 *
 * 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致;
 * 处理器在进行重排序时必须要考虑指令之间的数据依赖性;
 * 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。
 */
public class ResortSeqDemo {

   volatile int a=0;
    boolean flag=false;
    /*
    多线程下flag=true可能先执行,还没走到a=1就被挂起。
    其它线程进入method02的判断,修改a的值=5,而不是6。
     */
    public void method01(){
        a=1;
        flag=true;
    }
    public void method02(){
        if (flag){
            a+=5;
            System.out.println("*****retValue: "+a);
        }
    }

    public static void main(String[] args) {
        ResortSeqDemo resortSeq = new ResortSeqDemo();

        new Thread(()->{resortSeq.method01();},"ThreadA").start();
        new Thread(()->{resortSeq.method02();},"ThreadB").start();
    }
}

3.3.2.2.案例分析

为什么volatile 可实现禁⽌指令重排优化,从⽽避免多线程环境下程序出现乱序执⾏的现象?说说它的原理
我们先来了解⼀个概念,内存屏障(Memory Barrier)⼜称内存栅栏,是⼀个CPU指令,volatile底层就是⽤CPU的内存屏障(Memory Barrier)指令来实现的,它有两个作⽤

  • ⼀个是保证特定操作的顺序性
  • ⼆是保证变量的可⻅性。
    在这里插入图片描述
    在这里插入图片描述
    由于编译器和处理器都能够执⾏指令重排优化。所以,如果在指令间插⼊⼀条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排序,也就是说通过插⼊内存屏障可以禁⽌在内存屏障前后的指令进⾏重排序优化。内存屏障另外⼀个作⽤是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读到这些数据的最新版本。

四 哪些地⽅⽤到过volatile?

4.1.单例模式的安全问题

4.1.1传统单线程方式

4.1.1.1案例代码


/**
 * 单例设计模式的安全问题
 * 常见的DCL(Double Check Lock)双端检查模式虽然加了同步,但是在多线程下依然会有线程安全问题。
 */
public class SingletonDemo {
    private static SingletonDemo instance = null;

    private SingletonDemo() {
        System.out.println(Thread.currentThread().getName() + "\tSingletonDemo构造⽅法执⾏了");
    }

	public static SingletonDemo getInstance() {
        if (instance == null) {
                if (instance == null) {
                    instance = new SingletonDemo();
                
            }
        }
        return instance;
    }

    public static void main(String[] args) {
//main线程操作
        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
    }
}

4.1.1.2案例分析

单线程的情况下构造方法执行一次,没有人和他抢,不会出现任何问题
在这里插入图片描述

4.1.2.多线程方式

4.1.2.1.案例代码

public class SingletonDemo {
    private static SingletonDemo instance = null;

    private SingletonDemo() {
        System.out.println(Thread.currentThread().getName() + "\tSingletonDemo构造⽅法执⾏了");
    }

    public static SingletonDemo getInstance() {
        if (instance == null) {
            instance = new SingletonDemo();
        }
        return instance;
    }

    public static void main(String[] args) {
        //多线程操作
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                SingletonDemo.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}

4.1.2.2.案例分析

在这里插入图片描述
进过多次测试发现构造方法并不是只执行一次,也就说明有多个对象了,这违背了单例模式的初衷。

4.1.3.多线程方式–synchronized加锁

在创建对象的方法上加一把锁,这种方式虽然解决了只创建一个对象,但是别忘了在多线程情况下,这种暴力加锁,严重影响性能。

4.1.3.1.案例代码

public class SingletonDemo {
    private static SingletonDemo instance = null;

    private SingletonDemo() {
        System.out.println(Thread.currentThread().getName() + "\tSingletonDemo构造⽅法执⾏了");
    }

    public static synchronized SingletonDemo getInstance() {
        if (instance == null) {
            instance = new SingletonDemo();
        }
        return instance;
    }

    public static   void main(String[] args) {
        //多线程操作
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                SingletonDemo.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}

4.1.3.2.案例分析

在创建对象的方法上加一把锁,这种方式虽然解决了只创建一个对象,但是别忘了在多线程情况下,这种暴力加锁,严重影响性能。
在这里插入图片描述

4.1.4.多线程方式–DCL(Double Check Lock)双端检查模式

调整后,采⽤常⻅的DCL(Double Check Lock)双端检查模式加了同步,但是在多线程下依然会有线程安全问题。

4.1.4.1.案例代码

public class SingletonDemo {
    private static SingletonDemo instance = null;

    private SingletonDemo() {
        System.out.println(Thread.currentThread().getName() + "\tSingletonDemo构造⽅法执⾏了");
    }

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

        return instance;
    }

    public static   void main(String[] args) {
        //多线程操作
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                SingletonDemo.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}

4.1.4.2.案例分析

在这里插入图片描述
这个漏洞⽐较tricky,很难捕捉,但是是存在的。 instance=new SingletonDemo();通过查看SingletonDemo()对应的二进制文件,可以⼤致分为三步

在这里插入图片描述
在这里插入图片描述
剖析:
在多线程的环境下,由于有指令重排序的存在,DCL(双端检锁)机制不⼀定线程安全,我们可以加⼊volatile可以禁⽌指令重排。
原因在与某⼀个线程执⾏到第⼀次检测,读取到的instance不为null时,instance的引⽤对象可能没有完成初始化。
在这里插入图片描述
步骤2和步骤3不存在数据依赖关系,⽽且⽆论重排前还是重排后,程序的执⾏结果在单线程中并没有改变,因此这种重排优化是允许的。
在这里插入图片描述
但是指令重排只会保证串⾏语义的执⾏⼀致性(单线程),并不关⼼多线程的语义⼀致性。所以,当⼀条线程访问instance不为null时,由于instance实例未必已初始化完成,也就造成了线程安全问题

 public static  SingletonDemo getInstance() {
        if (instance==null) {
            synchronized (SingletonDemo.class){
                if (instance == null) {
                    //多线程情况下,可能发⽣指令重排
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

4.1.4.3.解决方案

如果发⽣指定重排,那么,

  1. 此时内存已经分配,那么 instance=memory不为null。
  2. 碰巧,若遇到线程此时挂起,那么 instance(memory)还未执⾏,对象还未初始化。
  3. 导致了 instance!=null ,所以两次判断都跳过,最后返回的 instance没有任何内容,还没初始化。

解决的⽅法就是对 singletondemo对象添加上volatile关键字,禁⽌指令重排。

   private volatile static SingletonDemo instance = null;

设计模式 - 单例模式(一)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/399476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker学习(二十一)构建 java 项目基础镜像

目录1.下载 JDK 包2.编写 Dockerfile3.构建镜像4.创建容器测试1.下载 JDK 包 JDK各版本官网下载地址&#xff1a; https://www.oracle.com/java/technologies/downloads/archive/#JavaSE 这里我们以 JDK 8u351 为例&#xff0c;点击 Java SE (8U211 and later)。 点击下载 jd…

Mysql问题:[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause

1 问题描述 使用Navicat连接到MySQL(版本&#xff1a;8.0.18)&#xff0c;执行查询&#xff1a; select * from t_user WHERE user_name admin查询结果没有问题&#xff0c;但是报错&#xff1a; [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY claus…

分布式 微服务

微服务学习 soa和微服务 业务系统实施服务化改造之后&#xff0c;原本共享的业务被拆分形成可复用的服务&#xff0c;可以在最大程度上避免共享业务的重复建设、资源连接瓶颈等问题。那么被拆分出来的服务是否也需要以业务功能为维度来进行拆分和独立部署&#xff0c;以降低业…

学习streamlit-4

st.slider 今天学习st.slider滑块组件的使用。 st.slider滑块组件通常被用来作为应用的输入&#xff0c;支持整数、浮点数、日期、时间和日期时间。 下面的示例程序包含以下简单功能&#xff0c;以演示st.slider滑块组件&#xff1a; 用户通过调整滑块选择值应用打印出所选…

C++面向对象编程之五:友元(friend)

C中&#xff0c;允许一个类的非共有成员被这个类授予友元&#xff08;friend&#xff09;关系的全局函数&#xff0c;另一个类&#xff0c;或另一个类中的成员函数访问。友元不是一个类中的成员&#xff0c;所以它们不受声明出现部分的访问权限&#xff08;public&#xff0c;p…

Binder通信原理与弊端解析

Binder 定义 简单来说&#xff0c;Binder 就是用来Client 端和 Server 端通信的。并且 Client 端和 Server 端 可以在一个进程也可以不在同一个进程&#xff0c;Client 可以向 Server 端发起远程调用&#xff0c;也可以向Server传输数据&#xff08;当作函数参数来传&#xff…

USART_GetITStatus与 USART_GetFlagStatus的区别

文章目录共同点不同点USART_GetITStatus函数详解USART_GetFlagStatus函数共同点 都能访问串口的SR寄存器 不同点 USART_GetFlagStatus(USART_TypeDef USARTx, uint16_t USART_FLAG)&#xff1a;* 该函数只判断标志位&#xff08;访问串口的SR寄存器&#xff09;。在没有使能…

TwinCAT3中ModbusTCP Server和C# Client连接

目录 一、硬件环境 1、设置PLC的ip地址 2、ModbusTCP软件安装 3、PLC操作系统防火墙设置 4、网络助手连接PLC 二、创建PLC工程 1、创建寄存器读写变量 2、添加ModbusTCP授权 3、激活和运行工程 三、ModbusTCP数据协议说明 1、写单个寄存器 2、读寄存器 &#xff08;1&…

反转链表相关的练习(下)

目录 一、回文链表 二、 重排链表 三、旋转链表 一、回文链表 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输…

安装mayavi的成功步骤

这篇文章是python 3.6版本&#xff0c;windows系统下的安装&#xff0c;其他python版本应该也可以&#xff0c;下载对应的包即可。 一定不要直接pip install mayavi&#xff0c;这个玩意儿对vtk的版本有要求。 下载whl包 搞了很久不行&#xff0c;咱也别费那个劲了&#xff0…

【2023】某python语言程序设计跟学第三周内容

目录1.数字类型与操作&#xff1a;整数&#xff1a;浮点数&#xff1a;复数数值运算操作符数字之间关系数值运算函数2.案例&#xff1a;天天向上的力量第一问&#xff1a;1‰的力量第二问&#xff1a;5‰和1%的力量第三问&#xff1a;工作日的力量第四问&#xff1a;工作日的努…

Dynamics365 本地部署整体界面

昨天已经登陆上去了然后今天开机突然又登陆不上去了 具体原因也不知道 然后我把注册插件删除又重新下载结果还是登陆不上去于是返回之前的断点就可以登陆上去了重复昨天的操作这里就不截图了6、注册新步骤右键单击&#xff08;插件&#xff09;BasicPlugin.FollowUpPlugin&…

MySQL 主备一致

MySQL 主备一致主备切换binlog 格式statementrowmixed生产格式循环复制问题主备切换 MySQL 主备切换流程 : 状态 1 : 客户端的读写都直接访问节点 A&#xff0c;而节点 B 是 A 的备库&#xff0c;只将 A 的更新都同步过来 , 并本地执行。来保持节点 B 和 A 的数据是相同当切换…

Python蓝桥杯训练:基本数据结构 [二叉树] 中

Python蓝桥杯训练&#xff1a;基本数据结构 [二叉树] 中 文章目录Python蓝桥杯训练&#xff1a;基本数据结构 [二叉树] 中一、[翻转二叉树](https://leetcode.cn/problems/invert-binary-tree/)二、[对称二叉树](https://leetcode.cn/problems/symmetric-tree/)三、[二叉树的最…

Vue3这样子写页面更快更高效

在开发管理后台过程中,一定会遇到不少了增删改查页面,而这些页面的逻辑大多都是相同的,如获取列表数据,分页,筛选功能这些基本功能。而不同的是呈现出来的数据项。还有一些操作按钮。 对于刚开始只有 1,2 个页面的时候大多数开发者可能会直接将之前的页面代码再拷贝多…

工作记录:调研monorepo和微前端

2023年1月。因工作项目需要&#xff0c;调研 monorepo 、微前端等技术。 任务 一直在做的 BI 项目&#xff0c;随着需求迭代&#xff0c;模块越来越多&#xff0c;项目越来越复杂、臃肿。 最近&#xff0c;前一阶段的开发工作基本结束。新模块还在设计阶段。借此契机&#xf…

进阶C语言——数据的存储【详解】

文章目录1. 数据类型介绍1.1 类型的基本归类2. 整形在内存中的存储2.1 原码、反码、补码2.2 大小端介绍2.3 练习3. 浮点型在内存中的存储3.1 一个例子3.2 浮点数存储的规则1. 数据类型介绍 前面我们已经学习了基本的内置类型&#xff1a; char //字符数据类型 short //短整型 …

学习ForkJoin

学习ForkJoin一、普通解决多线程方式1、案例一2、效果图二、ForkJoin一、普通解决多线程方式 1、案例一 大数据量的List问题处理&#xff0c;多线程分批处理&#xff0c;需要解决的问题&#xff1a; 下标越界。线程安全。数据丢失。 private static ThreadPoolExecutor thre…

链表OJ之 快慢指针法总结

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; 前言&#xff1a; 快慢指针指的是每次指针移动的步长&#xff0c;是解决链表相关的题目的一大利器&#xff0c;下面我将以例题的形式讲解快慢指针法。 目录 一. 链表的中间结点 思路&#xff1a; 代码实…

GMP调度模型总结

优秀文章 什么是GMP调度模型 Golang的一大特色就是Goroutine。Goroutine是Golang支持高并发的重要保障。Golang可以创建成千上万个Goroutine来处理任务&#xff0c;将这些Goroutine分配、负载、调度到处理器上采用的是G-M-P模型。 什么是Goroutine Goroutine Golang Coro…