java线程-Java内存模型

Java线程同步与内存模型详解 Java线程的生命周期包含六种核心状态:NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(定时等待)和TERMINATED(终止)。线程通过join()方法可实现同步等待,当线程调用t2.join()时,当前线程会阻塞直至t2完成。JMM内存模型采用主内存和工作内存分离架构,通过volatile、同步块和happens-before原则保证可见性与有序性。正确使用这些机制可避免线程同步问题和内存可见性问题,如未同步导致的执行顺序紊乱或共享 阅读详情

多线程编程bug源头

cpu,内存,I/O设备都在不断的迭代,不断朝着更快的方向努力,但是,在这个快速发展的过程中,有一个核心矛盾一直存在,就是这三者的速度差异。cpu > 内存 > i/0。根据木桶理论,程序整体的性能取决于最慢的操作-i/o设备的读写,也就是说单方面提高cpu性能是无效的。 为了平衡这三者的速度差异,计算机体系机构,操作系统,编译程序都做出了贡献,主要体现在:

  1. cpu增加了缓存,以均衡与内存的速度差异;
  2. 操作系统增加了进程,线程,以分时复用cpu,进而均衡cpu与i/o设备的速度差异;
  3. 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。

CPU缓存-可见性问题

在单核时代,所有的线程都在一颗cpu上运行,cpu缓存与内存的数据一致性容易解决,因为所有线程都操作同一颗cpu的缓存,一个线程对缓存的写,对另外一个线程来说一定是可见的。 一个线程对共享变量的修改,另外一个线程能够立刻看到,我们称之为可见性。

在多核时代,每颗cpu都有自己的缓存,这时cpu缓存与内存的数据一致性就没那么容易解决了。当多个线程在不同的cpu上运行时,这些线程操作的是不同的cpu缓存。假设线程a在cpu-1上运行,线程b在CPU-2上运行,此时线程a对变量v的操作对线程b来说是不可见的。

public class Test{
    private long count = 0;
    private void add(){
        int i = 0;
        while(i++ <= 10000 count public static long calc final test test='new' test thread t1='new' thread->{
            test.add();
        });
        
        Thread t2 = new Thread(()->{
            test.add();
        });
        
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();
        retun count;
    }
}
复制代码

上面的程序,如果在单核时代,那么结果毋庸置疑是20000,但在多核时代,最后结果是10000-20000之间的随机数。 我们假设t1和t2线程同时开始执行,那么第一次都会将count=0读到各自的cpu缓存里,执行完count += 1之后,各自的cpu缓存里的值都是1,而不是我们期望的2.之后由于各自的cpu缓存里都有count值,所以导致最终count的计算结果小于20000.这就是缓存的可见性问题。

线程切换-原子性问题

java并发程序都是基于多线程的,这样也会涉及到线程切换。执行count += 1的操作,至少需要三条cpu指令:

  1. 首先,需要把变量count 从内存中加载到cpu的寄存器。
  2. 在寄存器中执行 +1 操作。
  3. 将结果写入内存,缓存机制导致可能写入的是cpu的缓存还不是内存。

对于上面的三个指令来说,如果线程a刚刚执行完指令1,就和线程b发生了线程切换,导致count的值还是为0,而不是+1之后的值,就会导致其结果不是我们希望的2.

我们把一个或者多个操作在cpu执行的过程中不被中断的特性称为原子性。

编译执行-有序性问题

有序性指的是程序按照代码的先后顺序执行。 但是编译器为了优化性能,有时候会改变程序中语句的先后顺序。例如程序:“a = 6; b = 7”,编译优化后的顺序可能为:”b=7;a=6;“。 java中最经典的案例就是单例模式,利用双重检查创建单例对象,保证线程安全。

public class Singleton{
    static Singleton bean;
    static Singleton getInstance(){
        if(bean == null){
            synchronized(Singleton.class){
                if(bean == null){
                    bean = new Singleton();
                }
            }
        }
        return bean;
    }
}
复制代码

假设有两个线程a b同时调用getInstance()方法,于是同时对Singleton.class加锁,此时jvm保证只有一个线程能够加锁成功,假设是a,那么b就会处于等待状态,当a执行完,释放锁之后,B被唤醒,继续执行,发现已经有bean对象,所以直接return了。我们以为jvm创建对象的顺序是这样的:

  1. 分配一块内存m;
  2. 在内存m上初始化singleton对象;
  3. 然后m的地址赋值给bean变量;

但是实际上优化后的执行路径是这样的:

  1. 分配一块内存m;
  2. 将m的地址赋值给bean变量;
  3. 在内存M上初始化singleton对象;

当线程a先执行完getinstance()方法,当执行完指令2的时候,恰好发生了线程切换,切换到了线程b,如果此时线程B,也执行了getinstance方法,那么线程B在执行第一个判断的时候,就会发现bean!=null,直接return,如果这个时候我们访问bean对象的时候,就会报空指针异常。

实际上,在代码的编译执行过程中,有三种情况可能会导致指令重排

  1. 编译优化导致的重排序
  2. CPU指令并行执行导致的重排序
  3. 硬件内存模型导致的重排序

Java内存模型

从上面的分析,CPU缓存导致了可见性问题,线程切换导致了原子性问题,编译执行导致了有序性问题。那如何解决这三个问题呢?很直接的做法就是禁止使用CPU缓存,禁止线程切换,禁止指令重排。但是CPU缓存,线程切换,指令重排都是为了提高代码运行的效率,但为了保证多线程编程不会出现问题,过度禁止使用这些技术,也会影响代码执行效率,所以java内存模型就应运而生,对应的规范就是JSR-133。之所以叫java内存模型,是因为要解决的问题,都是跟内存有关。

Java内存模型解决多线程的3个问题,主要依靠3个关键词和1个规则,3个关键词分别是:volatile、synchronized、final,1个规则是:happens-before规则。

volatile

volatile关键字可以解决可见性、有序性和部分原子性问题。

volatile如何解决可见性问题

对于用volatile修饰的变量,在编译成机器指令时,会在写操作后面,加上一条特殊的指令:“lock addl #0x0, (%rsp)”,这条指令会将CPU对此变量的修改,立即写入内存,并通知其他CPU更新缓存数据。

volatile如何解决有序性问题

禁止指令重排序又分为完全禁止指令重排序和部分禁止指令重排序。完全禁止指令重排是指volatile修饰的变量的读写指令不可以跟其前面的读写指令重排,也不可以跟后面的读写指令重排。

指令重排是为了优化代码的执行效率,过于严格的限制指令重排,显然会降低代码的执行效率。因此,Java内存模型将volatile的语义定义为:部分禁止指令重排序。

对volatile修饰的变量执行写操作,Java内存模型只禁止位于其前面的读写操作与其进行重排序,位于其后面的读写操作可以与其进行指令重排序。

对volatile修饰的变量执行读操作,Java内存模型只禁止位于其后面的读写操作与其进行重排序,位于其前面的读写操作可以与其进行指令重排序。

为了能实现上述细化之后的指令重排禁止规则,Java内存模型定义了4个细粒度的内存屏障(Memory Barrier),也叫做内存栅栏(Memory Fence),它们分别是:StoreStore、StoreLoad、LoadLoad、LoadStore。

  • 在每个 volatile 写操作的前面插入一个 StoreStore 屏障。
  • 在每个 volatile 写操作的后面插入一个 StoreLoad 屏障。
  • 在每个 volatile 读操作的后面插入一个 LoadLoad 屏障。
  • 在每个 volatile 读操作的后面插入一个 LoadStore 屏障。
# other ops
[StoreStore]
x = 1 # volatile修饰x变量,volatile写操作
[StoreLoad]
y = x # volatile读操作
[LoadLoad]
[LoadStore]
# other ops
复制代码

volatile如何解决部分原子性问题

两类原子性问题,一类是64位long和double类型数据的读写的原子性问题,另一类是自增语句(例如count++)的原子性问题。volatile可以解决第一类原子性问题,但是无法解决第二类原子性问题。

synchronized

synchronized也可以解决可见性、有序性、原子性问题。只不过,它的解决方式比较简单粗暴,让原本并发执行的代码串行执行,并且,每次加锁和释放锁,都会同步CPU缓存和内存中的数据。

final

final修饰变量时,初衷是告诉编译器:这个变量生而不变,可以可劲儿优化,这就导致在1.5版本之前优化的很努力,以至于都优化错了,双重检索方法创建单例,构造函数的错误重排导致线程可能看到final变量的值会变化。但是1.5以后已经对final修饰的变量的重排进行了约束。

happens-before

概念:前面一个操作的结果对后续操作是可见的。也就是说,happens-before约束了编译器的优化行为,虽允许编译器优化,但是要求编译器优化后一定要遵守happens-before原则。

happens-before一共有六项规则:

程序的顺序性规则

前面的操作happens-before于后续的任意操作。

例如上面的代码 x=42happens-before于v=true;,比较符合单线程的思维,程序前面对某个变量的修改一定是对后续操作可见的。

volatile变量规则

对一个volatile变量的写操作,happens-befores与后续对这个变量的读操作。 这怎么看都有禁用缓存的意思,貌似和1.5版本之前的语义没有变化,这个时候我们需要关联下一个规则来看这条规则。

传递性

a happens-before于 b,b happens-before于c,那么a happens-before与c。

  1. x = 42 happens-before于 写变量v = true; —-规则1
  2. 写变量v = true happens-before于 读变量v==true。—-规则2
  3. 所以x = 42 happens-before于 读变量v == true;—-规则3

如果线程b读到了v= true,那么线程a设置的x=42对线程b是可见的。也就是说线程b能看到x=42。

管程中锁的原则

对一个锁的解锁happens-before于对后续这个锁的加锁操作。 synchronized是java里对管程的实现。

管程中的锁在java里是隐式实现的,例如下面的代码,在进入同步块之前,会自动加锁,而在代码块执行完会自动释放锁。加锁和解锁的操作都是编译器帮我们实现的。

synchronzied(this){// 此处自动加锁
    // x 是共享变量,初始值是10;
    if(this.x < 12){
        this.x = 12;
    }
}// 此处自动解锁
复制代码

根据管程中锁的原则,线程a执行完代码块后x的值变成12,线程B进入代码块时,能够看到线程a对x的写的操作,也就是线程b能看到x=12。

start()

主线程a启动子线程b后,子线程B能够看到主线程a在启动子线程b前的操作。也就是在线程a中启动了线程b,那么该start()操作happens-before于线程b中任意操作。

int x = 0;
Thread B = new Thread(()->{
    // 这里能看到变量x的值,x = 12;
});
x = 12;
B.start();
复制代码

join

主线程a等待子线程b完成(主线程a调用子线程b的join方法实现),当b完成后(主线程a中join方法返回),主线程a能够看到子线程b的任意操作。这里都是对共享变量的操作。

如果在线程a中调用了线程b的join()并成功返回,那么线程b中任意操作happens-before于该join操作的返回。

int x = 0;
Thread b = new Thread(()->{
    x = 11;
});

x = 12;
b.start();
b.join();
// x = 11;
复制代码

线程中断规则

线程a调用了线程b的interrupt()方法,happens-before于线程b的代码检测到中断事件的发生。

对象终结规则

一个对象初始化完成,happens-before于它的finalize()方法的调用

CPU缓存一致性协议与可见性

CPU缓存一致性协议

目的是为了保证不同CPU之间的缓存数据一致,比较经典的一致性协议就是MESI协议。

缓存行有4种不同的状态:

  • 已修改Modified (M)
  • 缓存行是脏的(dirty),与主存的值不同。如果别的CPU内核要读主存这块数据,该缓存行必须回写到主存,状态变为共享(S).
  • 独占Exclusive (E)
  • 缓存行只在当前缓存中,但是干净的(clean)--缓存数据同于主存数据。当别的缓存读取它时,状态变为共享;当前写数据时,变为已修改状态。
  • 共享Shared (S)
  • 缓存行也存在于其它缓存中且是干净的。缓存行可以在任意时刻抛弃。
  • 无效Invalid (I)
  • 缓存行是无效的

我们通过一个例子来深入了解一下MESI缓存行4种不同状态的转移方式,例如我们有3个CPU,分别为CPU0,CPU1,CPU2,初始变量V=1。

  1. 第一步,CPU0读取V,CPU0 Cache里的V=1,状态为E。其余CPU Cache没有数据
  2. 第二步,CPU0更新V=2,CPU0 Cache里的V=2,状态为M,Memory里V = 1。其余CPU Cache没有数据
  3. 第三步,CPU1读取V,先通过总线广播一条读请求到其他CPU,CPU0接收到通知后,状态为M,需要将更新后的数据更新到住内存,再将状态变为S,才能响应CPU1的读取请求,并将CPU1 Cache里的缓存行的变为S。
  4. 第四步,CPU2读取V,同第三步
  5. 第五步,CPU2更新V,因为CPU2缓存行里的状态为S,如果需要修改V,则需要先广播其他缓存行状态为S的CPU Cache,将其他CPU上对应的缓存行的状态改为I,并回复invalidate ack消息给CPU2,CPU2收到invalidate ack消息后,更新数据V=3,同步更新到主内存上,CPU2上的缓存行状态则变为E。
  6. 第六步,CPU0读取,发现其CPU缓存行的状态为I,所以CPU0先广播读请求,CPU1不做处理,CPU2上将缓存行的状态改为S,CPU0从内存中读取,并更新缓存行状态为S。

Store Buffer

从上述第五步中我们可以了解到,当多个CPU共享同一个数据,其中一个CPU更新数据,需要先广播invalidate消息,其他CPU收到invalidate消息后将缓存行状态改为I,然后返回invalidate ack消息给这个CPU,然后这个CPU收到invalidate ack消息后,才能更新数据并同步更新到内存上。这个是非常耗时的一个操作,需要等CPU写操作完成后才能执行其他指令,会影响CPU的执行效率。所以计算机科学家,在CPU和CPU缓存之间增加了Store Buffer,用于完成异步写操作。

CPU会将写操作的信息存储到Store Buffer后,CPU就可以执行其他操作指令,Store Buffer负责完成广播invalidate消息,接收invalidate ack消息,写入缓存和内存等。

读取消息的时候也是先从Store Buffer里获取,如果Store Buffer里没有再从缓存和主存里获取。

Invalidate Queue

Store Buffer发送给其他CPU invalidate消息之后,需要等待其他CPU设置缓存失效并返回invalidate ack消息,才能执行写入缓存和内存的操作。但是其他CPU可能忙于执行其他指令,所以导致store buffer写入缓存和内存操作不及时,有大量的写操作信息存储在Store Buffer里。

你也许会想到可以扩大Store Buffer的存储空间,来让Store Buffer存储更多的写操作信息。

计算机科学家则是在Cpu Cache和总线之间设计了一个Invalidate Queue,用于存储invalidate消息和返回invalidate ack消息,并异步执行缓存行状态设置为I的操作。

CPU缓存一致性协议与可见性

如果没有Store Buffer和Invalidate Queue,那么,缓存一致性协议是可以保证各个CPU缓存之间的数据一致性,也就不会存在可见性问题。但是,当引入Store Buffer和Invalidate Queue来异步执行写操作之后,即便使用缓存一致性协议,但各个CPU缓存之间仍然会存在短暂的数据不一致的情况,也就是会存在短暂的可见性问题。

可见性案例:

  1. CPU0和CPU1均读取了内存中的数据a=1到各自的缓存中,对应的缓存行状态均标记为S(共享)。CPU0执行写入操作a=2,为了提高写入的速度,CPU0将写入操作a=2存储到Store Buffer中后就立刻返回。假设此时Store Buffer还没有完成写入缓存和内存操作。
  2. CPU0读取数据,是直接从Store Buffer里获取到a=2。CPU1读取数据,发现Store Buffer里没有数据,就从缓存里读取到a = 1。此时出现缓存数据不一致的情况。
  3. 假设Cpu0的Store Buffer会发送消息给Cpu1的Invalidate Queue。在Invalidate Queue还没有将失效信息更新到Cpu1的缓存前,Cpu1还是读取不到最新值a=2。

将写操作写入Store Buffer到Invalidate Queue根据失效信息将Cpu缓存行的状态设置为I的这段时间内,多个Cpu之间的缓存数据会存在短暂不一致的情况

Java内存模型与线程 1. Java内存模型Java虚拟机规范中试图定义一种Java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致性的并发效果。在此之前,主流程序语言直接使用物理硬件(或者说是操作系统的内存模型),因此会由于不同平台上内存模型的差异,导致程序在一套平台上并发完全正常,而在另一套平台上并发访问却经常出错。1. 主内存与工作内存Java内存模型的主要目标是定义程 阅读详情

相关推荐

Java 线程内存模型

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

很酷的站长的博客 428

多线程陷阱:Java内存模型与可见性问题全解析

在并发编程中,可见性指的是在一个线程中对共享变量的修改,能够及时地被其他线程观察到。可见性问题发生时,一个线程的更改可能对其他线程不可见,导致程序运行出现错误行为。内存模型定义了不同线程如何交互和访问内存,以及变量如何被更新和共享。它是理解多线程编程中可见性(以及其他并发问题)的基础。

逆流的小鱼 1236

JVM内存模型与Java线程内存模型的区别

总的来说,JVM内存模型和Java线程内存模型是Java程序运行时的两个重要方面,它们共同构建了一个稳定、高效的Java运行环境。JVM内存模型提供了整体的框架,而Java线程内存模型则在多线程情境下保证了数据的正确性和可见性。它是线程私有的,随着线程的创建而创建,随着线程的销毁而销毁。JVM内存模型主要关注Java程序在运行时的内存组织,而Java线程内存模型关注多线程环境下的数据共享和同步机制。JVM内存模型是整个Java虚拟机的内存结构,而Java线程内存模型则是在多线程环境下对内存的一种抽象。

流楚丶格念的博客 1723

【Java线程】Java内存模型总结

学习资料:http://www.infoq.com/cn/articles/java-memory-model-1 Java的并发采用的是共享内存模型(而非消息传递模型),线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。 同步是显式进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行。 Java内存模型的抽象 Java线程之间

Alpha's 学习笔记 2万+

JAVA内存模型与线程

主内存与工作内存 java内存模型规定了所有的变量都存储在主内存(Main Memory)中。每条线程还有自己的工作内存(Working Memory),线程的工作内存中保存了被该线程使用的变量的主内存副本,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的数据。线程间变量值的传递均需要通过主内存来完成。 内存间交互操作 lock(锁定) 作用于主内存的变量,把一个变量标识为一条线程独占的状态 unlock(解锁) 作用于主内存的变量,把一个处于锁定状态的变量释放出

Happy Simon 1166

PCB阻焊工艺全解析:从油墨选择到显影测试的完整指南

本文深入解析PCB阻焊工艺全流程,从材料科学角度对比液态光成像(LPI)油墨、干膜阻焊等核心材料特性,系统阐述前处理、涂覆、预烤曝光、显影固化等关键步骤的工艺要点与参数控制。重点探讨了如何通过优化油墨选择与工艺参数,解决常见缺陷,提升PCB的可靠性、美观度及可制造性,为工程师提供从理论到产线实战的完整指南。

s3t4u的博客 554

Java线程内存模型和线程

引子 在物理机中,并发执行多个任务,充分利用计算机处理器的性能,看起来顺理成章,但实际上并不像看起来那么简单。其中一个重要的原因就是绝大多数的任务不能只靠处理器“计算”就能完成,处理器至少要与内存交互,比如读取、存储数据,这个I/O操作是很难消除的。而储存设备和处理器的运算速度差了好几个量级,所以引入了高速缓存(cache)来作为缓冲。即将运算要使用的数据复制到缓存,让运算快速进行,运算结束后从缓存同步回主存,这样处理器就无须等待缓慢的内存读写了。 虽然解决了处理器和内存的速度矛盾,但是引入了新的问题:

luzhensmart的专栏 241

InternVL 3.5 深度解析:开源多模态模型的新里程碑

InternVL 3.5是由上海人工智能实验室OpenGVLab团队推出的开源多模态大模型,在推理能力、效率和灵活性方面取得突破。该模型采用ViT-MLP-LLM架构,包含60亿参数的InternViT-6B视觉编码器和多种规格的语言模型骨干。核心创新包括:1)Cascade RL强化学习框架,通过离线热身和在线强化两阶段训练提升推理能力;2)ViR视觉分辨率路由器,动态调整图像处理分辨率以优化计算效率;3)DvD动态视觉token压缩技术。模型系列涵盖2B到241B参数规模,满足从边缘设备到高性能计算的不

m0_47999117的博客 1031

java的内存模型和线程调度

计算机同时处理多个任务,一方面是因为计算机的运算能力强大,另一方面,也有计算机运算速度和它存储与通信子系统是速度差距太大的原因,很多时间浪费在了IO读取,网络通信等任务上,如果无法并发将会浪费许多的运算资源。虽然高速缓存解决了处理器和内存速度的问题,但是多个处理器,每一个处理器都有自己的高速缓存,但是他们都共享同一内存,如果多个处理器处理同一块内存上的数据可能导致出现同步问题,为了保证一致性,计算机设置了缓存一致性协议,通过协议保证数据的同步。

m0_73986294的博客 1509

LangChain .NET:C#开发者构建AI应用的RAG框架与实战指南

大语言模型(LLM)作为当前人工智能的核心技术,通过深度学习海量文本数据,掌握了强大的语言理解和生成能力。其原理基于Transformer架构,通过自注意力机制捕捉长距离依赖关系,从而实现对复杂语义的建模。这一技术价值在于能够将非结构化的自然语言转化为可计算、可推理的表示,为各类智能应用提供认知基础。在实际工程实践中,LLM常被应用于构建检索增强生成(RAG)系统和AI智能体(Agent)。RAG通过结合外部知识库检索与LLM生成,有效提升了问答的准确性和事实性,是构建企业知识库、智能客服等场景的关键技术。

weixin_42538175的博客 706

matlab 点云可视化(1)——pcshow()函数可视化单个点云

matlab可视化单个点云常用方法汇总。

点云侠的博客 5362

java线程的内存模型_java内存模型与线程

1.硬件的效率与一致性由于计算机的存储设备与处理器的运算速度有几个数量级的差别,而绝大多数的运算任务都要与内存交互,所以现代计算机系统不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓冲之中,让运算能快速进行,当运算结束后再从缓存同步回内存之中,这样处理器就无须等待缓慢的内存读写了。基于高速缓存的存储交互很好地解决了处理器与内存的速...

weixin_39626298的博客 149

海思SD3403,SS928/926,hi3519dv500,hi3516dv500移植yolov7,yolov8(1)

海思SS928,SS926以及Hi3519dv500,hi3516dv500下的yolov7模型部署推理系列

Littro Innovations 3088

Java线程内存模型

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

Gonelin的博客 497

史上最全!研究生科研必备科研工具(含AI科研工具),附使用说明和示例

这些工具在科研中的应用非常广泛,可以帮助研究生更高效地完成研究任务。为了更好地利用这些工具,建议深入学习它们的官方文档和教程,或者参加相关的工作坊和培训。

智能AI让学术科研更容易 3945

Qt C++ 串口调试助手

1. **串口配置**:支持自动检测可用串口,可配置波特率(9600~921600)、数据位(5~8)、校验位(无/奇/偶/标记/空格)、停止位(1/1.5/2)。- HEX 模式发送时,输入格式需为十六进制字符(0-9、A-F),可带空格(如 `01 A3 FF`)或不带空格(如 `01A3FF`)。// 发送模式:true=HEX,false=ASCII。// 字符串转HEX字节数组。ui->parity->addItems({"无", "奇校验", "偶校验", "标记校验", "空格校验"});

寒寒豆豆软件公司的博客 148

Java 内存模型与线程

这篇文章是我之前看书时做的笔记,内容都是基于【深入理解 JVM 虚拟机】(周志明)这本书中介绍的东西,觉得有一定的价值所以拿出来分享。并且个人时间总是有限的,无法保证大量的产出,因此拿点之前的东西来充个数。 硬件的效率与一致性 由于计算的存储设备与处理器的运算速度有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间...

张可的博客 258

Winform从入门到精通(7)——ComboBox(史上最全)

本文介绍ComboBox控件的详细用法,包括所有属性、事件,绝对史上最全!

qq_34059233的博客 2万+

深入理解Java内存模型与线程

Java内存模型(JMM)是为了屏蔽底层硬件和操作系统的差异,使得Java程序在各种平台上都能获得一致的内存访问效果。随着多线程的普及,理解JMM以及Java线程的实现至关重要。本文将详细解析Java内存模型、线程实现及其状态转换。

dm菜鸟编程的专栏 1388

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

java线程的内存模型_Java线程内存模型

0x01 内存模型图的思维转换JVM定义了Java的虚拟内存模型,跟C/C++不一样的是,虚拟内存将物理内存划分了不同的区域,而C/C++是直接映射物理内存的。笼统的来说,jvm一般将内存分为栈和堆,栈用来存储静态方法和静态变量,而堆用来存储对象和普通变量。但是如果从线程的角度,内存模型会变成下图的样子线程内存模型在这个模型中,变量是在主内存中的,线程各自有各自的工作内存,不会出现相互干扰。工作内...

weixin_42554042的博客 333

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

java内存模型与线程

12.1 概述程序大量的时间花费在磁盘I/O、网络通信或者数据库访问上;衡量一个服务性能的高低好坏,每秒事务处理数(Transactions Per Second  TPS)是重要的指标之一。12.2 硬件的效率与一致性由于计算机的存储设备与处理器的运算速度有着几个数量级的差距,所以现代计算机系统都不得不加入一层或多层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲。将运算需要的数据复制到缓存,运算结束后再从缓存同步回内存之中。处理器可能会对输入代码进行乱序执行

yxkong的专栏 326

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Java线程内存模型,线程、工作内存、主内存

    java线程内存模型 线程、工作内存、主内存三者之间的交互关系图: key edeas 所有线程共享主内存,每个线程有自己的工作内存 refreshing local memory to/from main memory must comply to JMM rules 产生线程安全的原因 线程的working memory是cpu的寄存器和高速缓存的抽象描述:现在的计...

majiawenzzz的博客 6147
上一篇: 一线大厂Java八股文合集PDF版分享,内容多达700多页
下一篇: 某计科985研究生三面字节成功拿下实习岗面经+Java面试题分享!
程序员职业指南
博客等级 码龄4年 2138粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值